Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions site/lib/jaspr_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
import 'package:jaspr/jaspr.dart';
import 'package:docs_flutter_dev_site/src/client/global_scripts.dart'
as prefix0;
import 'package:docs_flutter_dev_site/src/components/client/on_this_page_button.dart'
import 'package:docs_flutter_dev_site/src/components/client/dartpad_injector.dart'
as prefix1;
import 'package:docs_flutter_dev_site/src/components/header/menu_toggle.dart'
import 'package:docs_flutter_dev_site/src/components/client/on_this_page_button.dart'
as prefix2;
import 'package:docs_flutter_dev_site/src/components/header/site_switcher.dart'
import 'package:docs_flutter_dev_site/src/components/header/menu_toggle.dart'
as prefix3;
import 'package:docs_flutter_dev_site/src/components/header/theme_switcher.dart'
import 'package:docs_flutter_dev_site/src/components/header/site_switcher.dart'
as prefix4;
import 'package:docs_flutter_dev_site/src/components/cookie_notice.dart'
import 'package:docs_flutter_dev_site/src/components/header/theme_switcher.dart'
as prefix5;
import 'package:docs_flutter_dev_site/src/components/copy_button.dart'
import 'package:docs_flutter_dev_site/src/components/cookie_notice.dart'
as prefix6;
import 'package:docs_flutter_dev_site/src/components/feedback.dart' as prefix7;
import 'package:docs_flutter_dev_site/src/components/copy_button.dart'
as prefix7;
import 'package:docs_flutter_dev_site/src/components/feedback.dart' as prefix8;

/// Default [JasprOptions] for use with your jaspr project.
///
Expand All @@ -43,45 +45,56 @@ JasprOptions get defaultJasprOptions => JasprOptions(
'src/client/global_scripts',
),

prefix1.OnThisPageButton: ClientTarget<prefix1.OnThisPageButton>(
prefix1.DartPadInjector: ClientTarget<prefix1.DartPadInjector>(
'src/components/client/dartpad_injector',
params: _prefix1DartPadInjector,
),

prefix2.OnThisPageButton: ClientTarget<prefix2.OnThisPageButton>(
'src/components/client/on_this_page_button',
),

prefix5.CookieNotice: ClientTarget<prefix5.CookieNotice>(
prefix6.CookieNotice: ClientTarget<prefix6.CookieNotice>(
'src/components/cookie_notice',
),

prefix6.CopyButton: ClientTarget<prefix6.CopyButton>(
prefix7.CopyButton: ClientTarget<prefix7.CopyButton>(
'src/components/copy_button',
params: _prefix6CopyButton,
params: _prefix7CopyButton,
),

prefix7.FeedbackComponent: ClientTarget<prefix7.FeedbackComponent>(
prefix8.FeedbackComponent: ClientTarget<prefix8.FeedbackComponent>(
'src/components/feedback',
params: _prefix7FeedbackComponent,
params: _prefix8FeedbackComponent,
),

prefix2.MenuToggle: ClientTarget<prefix2.MenuToggle>(
prefix3.MenuToggle: ClientTarget<prefix3.MenuToggle>(
'src/components/header/menu_toggle',
),

prefix3.SiteSwitcher: ClientTarget<prefix3.SiteSwitcher>(
prefix4.SiteSwitcher: ClientTarget<prefix4.SiteSwitcher>(
'src/components/header/site_switcher',
),

prefix4.ThemeSwitcher: ClientTarget<prefix4.ThemeSwitcher>(
prefix5.ThemeSwitcher: ClientTarget<prefix5.ThemeSwitcher>(
'src/components/header/theme_switcher',
),
},
styles: () => [],
);

Map<String, dynamic> _prefix6CopyButton(prefix6.CopyButton c) => {
Map<String, dynamic> _prefix1DartPadInjector(prefix1.DartPadInjector c) => {
'title': c.title,
'theme': c.theme,
'height': c.height,
'runAutomatically': c.runAutomatically,
};
Map<String, dynamic> _prefix7CopyButton(prefix7.CopyButton c) => {
'toCopy': c.toCopy,
'buttonText': c.buttonText,
'classes': c.classes,
'title': c.title,
};
Map<String, dynamic> _prefix7FeedbackComponent(prefix7.FeedbackComponent c) => {
Map<String, dynamic> _prefix8FeedbackComponent(prefix8.FeedbackComponent c) => {
'issueUrl': c.issueUrl,
};
121 changes: 121 additions & 0 deletions site/lib/src/components/client/dartpad_injector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2025 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:jaspr/jaspr.dart';
import '../dartpad/embedded_dartpad.dart';

import '../dartpad/extract_content.dart'
if (dart.library.io) '../dartpad/extract_content_vm.dart';

/// Prepares a code block that will be replaced with an embedded
/// DartPad when the site is loaded.
final class DartPadWrapper extends StatefulComponent {
DartPadWrapper({
super.key,
required this.content,
required this.title,
this.theme,
this.height,
this.runAutomatically = false,
});

final String content;
final String title;
final String? theme;
final String? height;
final bool runAutomatically;

@override
State<StatefulComponent> createState() => _DartPadWrapperState();
}

final class _DartPadWrapperState extends State<DartPadWrapper> {
@override
Component build(BuildContext context) {
return DartPadInjector(
title: component.title,
theme: component.theme,
height: component.height,
runAutomatically: component.runAutomatically,
// We don't pass the content here, so it's not part of the client
// component data. It will be retrieved by DartPadInjector automatically.
);
}
}

@client
class DartPadInjector extends StatefulComponent {
const DartPadInjector({
required this.title,
this.theme,
this.height,
this.runAutomatically = false,
super.key,
});

final String title;
final String? theme;
final String? height;
final bool runAutomatically;

@override
State<DartPadInjector> createState() => _DartPadInjectorState();
}

class _DartPadInjectorState extends State<DartPadInjector> {
static int _injectedIndex = 0;

final String _frameId = () {
final nextId = _injectedIndex;
_injectedIndex += 1;
return 'embedded-dartpad-$nextId';
}();

String content = '';

@override
void initState() {
super.initState();

if (kIsWeb) {
// During hydration, extract the content from the pre-rendered code block.
content = extractContent(context as Element);
}
}

@override
Component build(BuildContext context) {
if (!kIsWeb) {
// During pre-rendering, get the content from the nearest DartPadWrapper.
final content = context
.findAncestorStateOfType<_DartPadWrapperState>()
?.component
.content;
return pre([
code(
attributes: {'title': component.title},
[text(content ?? '')],
),
]);
}

return Component.wrapElement(
attributes: {
'height': ?component.height,
'title': component.title,
},
child: EmbeddedDartPad.create(
iframeId: _frameId,
theme: switch (component.theme) {
'auto' => DartPadTheme.auto,
'dark' => DartPadTheme.dark,
_ => DartPadTheme.light,
},
embedLayout: true,
runAutomatically: component.runAutomatically,
code: content,
),
);
}
}
Loading