dr-widget 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dr_widget-0.2.0 → dr_widget-0.2.2}/PKG-INFO +10 -28
- {dr_widget-0.2.0 → dr_widget-0.2.2}/README.md +9 -27
- {dr_widget-0.2.0 → dr_widget-0.2.2}/pyproject.toml +7 -2
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/.gitignore +2 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/index.html +30 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/package.json +25 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/parity.html +30 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/components/Hello.tsx +8 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/components/PropsPanel.tsx +30 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/data-channel.ts +52 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/define-element.tsx +127 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/globals.d.ts +23 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/index.tsx +17 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/layout.ts +41 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/src/parse-props.ts +85 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/static/runtime.js +40 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/tsconfig.json +20 -0
- dr_widget-0.2.2/src/dr_widget/bundled/runtime/vite.config.js +24 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/inline/__init__.py +2 -1
- dr_widget-0.2.2/src/dr_widget/inline/runtime_loader.py +40 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/__init__.py +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/__init__.py +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/.gitignore +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/.vscode/extensions.json +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/README.md +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/__init__.py +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/components.json +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/index.html +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/jsrepo.json +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/package.json +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/postcss.config.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/public/fonts/Inter-roman.var.woff2 +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/public/vite.svg +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/App.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/ConfigFileManager.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/app.css +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/index.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/@test_state.json +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/Counter.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/BrowseConfigsPanel.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/ComplexJsonViewer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/ConfigViewerPanel.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/LoadedConfigPreview.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/SaveConfigPanel.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/SelectedFileRow.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/SelectedFilesList.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/file-drop/SimpleJsonViewer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/badge/badge.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/badge/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/button/button.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/button/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card-action.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card-footer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card-header.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/card.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/card/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-close.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-footer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-header.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-overlay.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/dialog-trigger.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/dialog/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-close.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-footer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-header.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-nested.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-overlay.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer-trigger.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/drawer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/drawer/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/empty-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/empty-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/empty-header.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/empty-media.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/empty-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/empty.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/empty/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-error.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-group.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-label.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-legend.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-separator.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-set.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/field.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/field/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/file-drop-zone/file-drop-zone.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/file-drop-zone/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/file-drop-zone/types.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-actions.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-footer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-group.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-header.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-media.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-separator.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/item/item.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/label/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/label/label.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal-description.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal-footer.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal-header.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal-title.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal-trigger.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/modal/modal.svelte.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/separator/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/separator/separator.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/tabs/index.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/tabs/tabs-content.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/tabs/tabs-list.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/tabs/tabs-trigger.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/components/ui/tabs/tabs.svelte +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/hooks/use-file-bindings.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/react/JsonTreeCanvas.tsx +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/utils/config-format.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/utils/utils.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/lib/utils.ts +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/src/main.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/static/fonts/Inter-roman.var.woff2 +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/static/index.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/static/style.css +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/static/vite.svg +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/svelte.config.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/tailwind.config.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/tsconfig.json +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/bundled/config_file_manager/vite.config.js +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/inline/active_html.py +0 -0
- {dr_widget-0.2.0 → dr_widget-0.2.2}/src/dr_widget/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: dr-widget
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Widgets to use with marimo notebooks
|
|
5
5
|
Author: Danielle Rothermel
|
|
6
6
|
Author-email: Danielle Rothermel <danielle.rothermel@gmail.com>
|
|
@@ -22,35 +22,18 @@ The top-level `dr_widget` package is intentionally empty so importing from one t
|
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
bun install
|
|
28
|
-
|
|
29
|
-
# Live-reload the Config File Manager widget in a browser
|
|
30
|
-
bun run dev:config-file-manager
|
|
31
|
-
|
|
32
|
-
# Produce the optimized bundle used by AnyWidget
|
|
33
|
-
bun run build
|
|
34
|
-
|
|
35
|
-
# Build the Python distributions (wheel + sdist)
|
|
36
|
-
uv build
|
|
37
|
-
|
|
38
|
-
# Launch the Marimo demo notebook
|
|
39
|
-
marimo run notebooks/config_file_manager_widget.py
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Prerequisites: Bun ≥ 1.0, Node-compatible environment, Python ≥ 3.11 with `uv`, and Marimo ≥ 0.23.
|
|
25
|
+
See [Development Workflow](docs/development.md) for install, build, packaging, and
|
|
26
|
+
notebook commands.
|
|
43
27
|
|
|
44
28
|
## Repository Layout
|
|
45
29
|
|
|
46
30
|
- `src/dr_widget/` – Python package with two widget tiers.
|
|
47
|
-
- `inline/` – pure-Python AnyWidgets (e.g., `ActiveHtml`)
|
|
48
|
-
- `bundled/config_file_manager/` –
|
|
49
|
-
|
|
50
|
-
- `src/lib/hooks/use-file-bindings.ts` – shared logic for syncing AnyWidget traitlets.
|
|
51
|
-
- `src/lib/components/` – shadcn-style UI primitives and panels, including a config viewer card with both a tree view and graph view for JSON payloads.
|
|
31
|
+
- `inline/` – pure-Python AnyWidgets (e.g., `ActiveHtml`, `load_dr_runtime()`).
|
|
32
|
+
- `bundled/config_file_manager/` – Svelte widget workspace (source in `src/`, build output in `static/`).
|
|
33
|
+
- `bundled/runtime/` – React custom-element runtime (`static/runtime.js`); proof element `<dr-hello>`.
|
|
52
34
|
- `docs/` – additional reference material (architecture, development workflows).
|
|
53
|
-
- `notebooks/config_file_manager_widget.py` – Marimo notebook
|
|
35
|
+
- `notebooks/config_file_manager_widget.py` – Marimo notebook for the Config File Manager widget.
|
|
36
|
+
- `notebooks/runtime_hello_widget.py` – Marimo notebook for `load_dr_runtime()` + `<dr-hello>`.
|
|
54
37
|
|
|
55
38
|
## Documentation
|
|
56
39
|
|
|
@@ -61,8 +44,7 @@ Prerequisites: Bun ≥ 1.0, Node-compatible environment, Python ≥ 3.11 with `u
|
|
|
61
44
|
## Contributing
|
|
62
45
|
|
|
63
46
|
1. Work inside a dedicated branch.
|
|
64
|
-
2.
|
|
65
|
-
3. Update
|
|
66
|
-
4. Follow the commit and PR practices outlined in `AGENTS.md`.
|
|
47
|
+
2. Follow [docs/development.md](docs/development.md) and [AGENTS.md](AGENTS.md) before opening a PR.
|
|
48
|
+
3. Update notebooks and docs when you add or change widget behaviour.
|
|
67
49
|
|
|
68
50
|
Please open an issue if you hit build problems or want to discuss new widgets.
|
|
@@ -9,35 +9,18 @@ The top-level `dr_widget` package is intentionally empty so importing from one t
|
|
|
9
9
|
|
|
10
10
|
## Quick Start
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
bun install
|
|
15
|
-
|
|
16
|
-
# Live-reload the Config File Manager widget in a browser
|
|
17
|
-
bun run dev:config-file-manager
|
|
18
|
-
|
|
19
|
-
# Produce the optimized bundle used by AnyWidget
|
|
20
|
-
bun run build
|
|
21
|
-
|
|
22
|
-
# Build the Python distributions (wheel + sdist)
|
|
23
|
-
uv build
|
|
24
|
-
|
|
25
|
-
# Launch the Marimo demo notebook
|
|
26
|
-
marimo run notebooks/config_file_manager_widget.py
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Prerequisites: Bun ≥ 1.0, Node-compatible environment, Python ≥ 3.11 with `uv`, and Marimo ≥ 0.23.
|
|
12
|
+
See [Development Workflow](docs/development.md) for install, build, packaging, and
|
|
13
|
+
notebook commands.
|
|
30
14
|
|
|
31
15
|
## Repository Layout
|
|
32
16
|
|
|
33
17
|
- `src/dr_widget/` – Python package with two widget tiers.
|
|
34
|
-
- `inline/` – pure-Python AnyWidgets (e.g., `ActiveHtml`)
|
|
35
|
-
- `bundled/config_file_manager/` –
|
|
36
|
-
|
|
37
|
-
- `src/lib/hooks/use-file-bindings.ts` – shared logic for syncing AnyWidget traitlets.
|
|
38
|
-
- `src/lib/components/` – shadcn-style UI primitives and panels, including a config viewer card with both a tree view and graph view for JSON payloads.
|
|
18
|
+
- `inline/` – pure-Python AnyWidgets (e.g., `ActiveHtml`, `load_dr_runtime()`).
|
|
19
|
+
- `bundled/config_file_manager/` – Svelte widget workspace (source in `src/`, build output in `static/`).
|
|
20
|
+
- `bundled/runtime/` – React custom-element runtime (`static/runtime.js`); proof element `<dr-hello>`.
|
|
39
21
|
- `docs/` – additional reference material (architecture, development workflows).
|
|
40
|
-
- `notebooks/config_file_manager_widget.py` – Marimo notebook
|
|
22
|
+
- `notebooks/config_file_manager_widget.py` – Marimo notebook for the Config File Manager widget.
|
|
23
|
+
- `notebooks/runtime_hello_widget.py` – Marimo notebook for `load_dr_runtime()` + `<dr-hello>`.
|
|
41
24
|
|
|
42
25
|
## Documentation
|
|
43
26
|
|
|
@@ -48,8 +31,7 @@ Prerequisites: Bun ≥ 1.0, Node-compatible environment, Python ≥ 3.11 with `u
|
|
|
48
31
|
## Contributing
|
|
49
32
|
|
|
50
33
|
1. Work inside a dedicated branch.
|
|
51
|
-
2.
|
|
52
|
-
3. Update
|
|
53
|
-
4. Follow the commit and PR practices outlined in `AGENTS.md`.
|
|
34
|
+
2. Follow [docs/development.md](docs/development.md) and [AGENTS.md](AGENTS.md) before opening a PR.
|
|
35
|
+
3. Update notebooks and docs when you add or change widget behaviour.
|
|
54
36
|
|
|
55
37
|
Please open an issue if you hit build problems or want to discuss new widgets.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dr-widget"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "Widgets to use with marimo notebooks"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -19,10 +19,15 @@ requires = ["uv_build>=0.9.7,<0.10.0"]
|
|
|
19
19
|
build-backend = "uv_build"
|
|
20
20
|
|
|
21
21
|
[tool.uv.build-backend]
|
|
22
|
-
source-include = [
|
|
22
|
+
source-include = [
|
|
23
|
+
"src/dr_widget/bundled/config_file_manager/static/**",
|
|
24
|
+
"src/dr_widget/bundled/runtime/static/**",
|
|
25
|
+
]
|
|
23
26
|
source-exclude = [
|
|
24
27
|
"src/dr_widget/bundled/config_file_manager/node_modules",
|
|
25
28
|
"src/dr_widget/bundled/config_file_manager/node_modules/**",
|
|
29
|
+
"src/dr_widget/bundled/runtime/node_modules",
|
|
30
|
+
"src/dr_widget/bundled/runtime/node_modules/**",
|
|
26
31
|
]
|
|
27
32
|
|
|
28
33
|
[dependency-groups]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>dr-hello runtime demo</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>dr_widget runtime demo</h1>
|
|
10
|
+
<p>
|
|
11
|
+
<dr-hello id="hello" name="World"></dr-hello>
|
|
12
|
+
</p>
|
|
13
|
+
<label>
|
|
14
|
+
Name
|
|
15
|
+
<input id="name-input" type="text" value="World" />
|
|
16
|
+
</label>
|
|
17
|
+
<button id="apply-name" type="button">Apply name attribute</button>
|
|
18
|
+
|
|
19
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
20
|
+
<script type="module">
|
|
21
|
+
const hello = document.getElementById('hello');
|
|
22
|
+
const input = document.getElementById('name-input');
|
|
23
|
+
const button = document.getElementById('apply-name');
|
|
24
|
+
|
|
25
|
+
button.addEventListener('click', () => {
|
|
26
|
+
hello.setAttribute('name', input.value);
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dr-widget/runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"dev": "vite dev",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"react": "18.2.0",
|
|
13
|
+
"react-dom": "18.2.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/bun": "latest",
|
|
17
|
+
"@types/react": "18.2.74",
|
|
18
|
+
"@types/react-dom": "18.2.25",
|
|
19
|
+
"@vitejs/plugin-react-swc": "3.7.0",
|
|
20
|
+
"vite": "^5.4.11"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>dr-hello built runtime parity</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>Built runtime parity</h1>
|
|
10
|
+
<p>
|
|
11
|
+
<dr-hello id="hello" name="World"></dr-hello>
|
|
12
|
+
</p>
|
|
13
|
+
<label>
|
|
14
|
+
Name
|
|
15
|
+
<input id="name-input" type="text" value="World" />
|
|
16
|
+
</label>
|
|
17
|
+
<button id="apply-name" type="button">Apply name attribute</button>
|
|
18
|
+
|
|
19
|
+
<script src="./static/runtime.js"></script>
|
|
20
|
+
<script>
|
|
21
|
+
const hello = document.getElementById('hello');
|
|
22
|
+
const input = document.getElementById('name-input');
|
|
23
|
+
const button = document.getElementById('apply-name');
|
|
24
|
+
|
|
25
|
+
button.addEventListener('click', () => {
|
|
26
|
+
hello.setAttribute('name', input.value);
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type PropsPanelProps = {
|
|
2
|
+
title?: string;
|
|
3
|
+
items?: string[];
|
|
4
|
+
__dataRef?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function PropsPanel({ title, items }: PropsPanelProps) {
|
|
8
|
+
const heading =
|
|
9
|
+
typeof title === 'string' && title.trim().length > 0
|
|
10
|
+
? title.trim()
|
|
11
|
+
: 'Props panel';
|
|
12
|
+
const rows = Array.isArray(items)
|
|
13
|
+
? items.filter((item): item is string => typeof item === 'string')
|
|
14
|
+
: [];
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<strong>{heading}</strong>
|
|
19
|
+
{rows.length > 0 ? (
|
|
20
|
+
<ul>
|
|
21
|
+
{rows.map((item) => (
|
|
22
|
+
<li key={item}>{item}</li>
|
|
23
|
+
))}
|
|
24
|
+
</ul>
|
|
25
|
+
) : (
|
|
26
|
+
<p>No items.</p>
|
|
27
|
+
)}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type DataChannelListener = (payload: unknown) => void;
|
|
2
|
+
|
|
3
|
+
export type DataChannel = {
|
|
4
|
+
set: (id: string, payload: unknown) => void;
|
|
5
|
+
get: (id: string) => unknown;
|
|
6
|
+
delete: (id: string) => void;
|
|
7
|
+
subscribe: (id: string, listener: DataChannelListener) => () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function createDataChannel(): DataChannel {
|
|
11
|
+
const payloads = new Map<string, unknown>();
|
|
12
|
+
const listeners = new Map<string, Set<DataChannelListener>>();
|
|
13
|
+
|
|
14
|
+
function notify(id: string, payload: unknown): void {
|
|
15
|
+
const subs = listeners.get(id);
|
|
16
|
+
if (!subs) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
for (const listener of subs) {
|
|
20
|
+
listener(payload);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
set(id, payload) {
|
|
26
|
+
payloads.set(id, payload);
|
|
27
|
+
notify(id, payload);
|
|
28
|
+
},
|
|
29
|
+
get(id) {
|
|
30
|
+
return payloads.get(id);
|
|
31
|
+
},
|
|
32
|
+
delete(id) {
|
|
33
|
+
payloads.delete(id);
|
|
34
|
+
notify(id, undefined);
|
|
35
|
+
},
|
|
36
|
+
subscribe(id, listener) {
|
|
37
|
+
let subs = listeners.get(id);
|
|
38
|
+
if (!subs) {
|
|
39
|
+
subs = new Set();
|
|
40
|
+
listeners.set(id, subs);
|
|
41
|
+
}
|
|
42
|
+
subs.add(listener);
|
|
43
|
+
const currentSubs = subs;
|
|
44
|
+
return () => {
|
|
45
|
+
currentSubs.delete(listener);
|
|
46
|
+
if (currentSubs.size === 0 && listeners.get(id) === currentSubs) {
|
|
47
|
+
listeners.delete(id);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRoot,
|
|
3
|
+
type ComponentType,
|
|
4
|
+
type Root,
|
|
5
|
+
} from 'react-dom/client';
|
|
6
|
+
|
|
7
|
+
import type { DataChannel } from './data-channel';
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_DATA_REF_ATTRIBUTE,
|
|
10
|
+
DEFAULT_PROPS_ATTRIBUTE,
|
|
11
|
+
parseElementProps,
|
|
12
|
+
readDataRef,
|
|
13
|
+
type ParsePropsOptions,
|
|
14
|
+
} from './parse-props';
|
|
15
|
+
|
|
16
|
+
export type DrElementOptions<P extends Record<string, unknown>> =
|
|
17
|
+
ParsePropsOptions & {
|
|
18
|
+
parseProps?: (
|
|
19
|
+
element: HTMLElement,
|
|
20
|
+
dataChannel: DataChannel,
|
|
21
|
+
) => P;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function isOptions<P extends Record<string, unknown>>(
|
|
25
|
+
value: readonly string[] | DrElementOptions<P>,
|
|
26
|
+
): value is DrElementOptions<P> {
|
|
27
|
+
return !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveOptions<P extends Record<string, unknown>>(
|
|
31
|
+
observedAttributesOrOptions: readonly string[] | DrElementOptions<P>,
|
|
32
|
+
): DrElementOptions<P> {
|
|
33
|
+
if (isOptions(observedAttributesOrOptions)) {
|
|
34
|
+
return observedAttributesOrOptions;
|
|
35
|
+
}
|
|
36
|
+
return { observedAttributes: observedAttributesOrOptions };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function defineDrElement<P extends Record<string, unknown>>(
|
|
40
|
+
tag: string,
|
|
41
|
+
Component: ComponentType<P>,
|
|
42
|
+
observedAttributesOrOptions: readonly string[] | DrElementOptions<P>,
|
|
43
|
+
dataChannel: DataChannel,
|
|
44
|
+
): void {
|
|
45
|
+
if (customElements.get(tag)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const options = resolveOptions(observedAttributesOrOptions);
|
|
50
|
+
const propsAttribute = options.propsAttribute ?? DEFAULT_PROPS_ATTRIBUTE;
|
|
51
|
+
const dataRefAttribute = options.dataRefAttribute ?? DEFAULT_DATA_REF_ATTRIBUTE;
|
|
52
|
+
|
|
53
|
+
const readProps = (element: HTMLElement): P => {
|
|
54
|
+
if (options.parseProps) {
|
|
55
|
+
return options.parseProps(element, dataChannel);
|
|
56
|
+
}
|
|
57
|
+
return parseElementProps(element, dataChannel, options) as P;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
class DrElement extends HTMLElement {
|
|
61
|
+
static get observedAttributes() {
|
|
62
|
+
const attrs = new Set<string>(options.observedAttributes ?? []);
|
|
63
|
+
attrs.add(propsAttribute);
|
|
64
|
+
attrs.add(dataRefAttribute);
|
|
65
|
+
return [...attrs];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#root: Root | null = null;
|
|
69
|
+
#unsubscribeData: (() => void) | null = null;
|
|
70
|
+
|
|
71
|
+
connectedCallback() {
|
|
72
|
+
this.#root = createRoot(this);
|
|
73
|
+
this.#bindDataChannel();
|
|
74
|
+
this.#render();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
attributeChangedCallback(
|
|
78
|
+
name: string,
|
|
79
|
+
oldValue: string | null,
|
|
80
|
+
newValue: string | null,
|
|
81
|
+
) {
|
|
82
|
+
if (oldValue === newValue) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!this.isConnected) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (name === dataRefAttribute) {
|
|
89
|
+
this.#bindDataChannel();
|
|
90
|
+
}
|
|
91
|
+
this.#render();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
disconnectedCallback() {
|
|
95
|
+
this.#unsubscribeData?.();
|
|
96
|
+
this.#unsubscribeData = null;
|
|
97
|
+
this.#root?.unmount();
|
|
98
|
+
this.#root = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#bindDataChannel() {
|
|
102
|
+
this.#unsubscribeData?.();
|
|
103
|
+
this.#unsubscribeData = null;
|
|
104
|
+
|
|
105
|
+
const dataRef = readDataRef(this, dataRefAttribute);
|
|
106
|
+
if (!dataRef) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.#unsubscribeData = dataChannel.subscribe(dataRef, () => {
|
|
111
|
+
this.#render();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#render() {
|
|
116
|
+
if (!this.#root) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.#root.render(<Component {...readProps(this)} />);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
customElements.define(tag, DrElement);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { whenLayoutReady } from './layout';
|
|
127
|
+
export type { LayoutReadyCallback } from './layout';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { DataChannel } from './data-channel';
|
|
4
|
+
import type { DrElementOptions } from './define-element';
|
|
5
|
+
|
|
6
|
+
export type DrRuntime = {
|
|
7
|
+
defineDrElement: <P extends Record<string, unknown>>(
|
|
8
|
+
tag: string,
|
|
9
|
+
Component: ComponentType<P>,
|
|
10
|
+
observedAttributesOrOptions: readonly string[] | DrElementOptions<P>,
|
|
11
|
+
) => void;
|
|
12
|
+
data: DataChannel;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
declare global {
|
|
17
|
+
interface Window {
|
|
18
|
+
__drRuntime?: DrRuntime;
|
|
19
|
+
__drRuntimeLoaded?: boolean;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Hello } from './components/Hello';
|
|
2
|
+
import { PropsPanel } from './components/PropsPanel';
|
|
3
|
+
import { createDataChannel } from './data-channel';
|
|
4
|
+
import { defineDrElement } from './define-element';
|
|
5
|
+
|
|
6
|
+
const RUNTIME_VERSION = '0.2.0';
|
|
7
|
+
const dataChannel = createDataChannel();
|
|
8
|
+
|
|
9
|
+
defineDrElement('dr-hello', Hello, ['name'], dataChannel);
|
|
10
|
+
defineDrElement('dr-props-panel', PropsPanel, [], dataChannel);
|
|
11
|
+
|
|
12
|
+
window.__drRuntime = {
|
|
13
|
+
defineDrElement: (tag, Component, observedAttributesOrOptions) =>
|
|
14
|
+
defineDrElement(tag, Component, observedAttributesOrOptions, dataChannel),
|
|
15
|
+
data: dataChannel,
|
|
16
|
+
version: RUNTIME_VERSION,
|
|
17
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type LayoutReadyCallback = (element: HTMLElement) => void;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run `callback` once the element has non-zero layout dimensions.
|
|
5
|
+
*
|
|
6
|
+
* Design rule 2: never read geometry in `connectedCallback` — layout may not
|
|
7
|
+
* be ready yet. Prefer this helper (rAF + ResizeObserver) for sizing work.
|
|
8
|
+
*/
|
|
9
|
+
export function whenLayoutReady(
|
|
10
|
+
element: HTMLElement,
|
|
11
|
+
callback: LayoutReadyCallback,
|
|
12
|
+
): () => void {
|
|
13
|
+
let cancelled = false;
|
|
14
|
+
let completed = false;
|
|
15
|
+
let observer: ResizeObserver | null = null;
|
|
16
|
+
|
|
17
|
+
const run = () => {
|
|
18
|
+
if (cancelled || completed) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (element.clientWidth > 0 || element.clientHeight > 0) {
|
|
22
|
+
completed = true;
|
|
23
|
+
callback(element);
|
|
24
|
+
observer?.disconnect();
|
|
25
|
+
observer = null;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
requestAnimationFrame(run);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
observer = new ResizeObserver(() => {
|
|
32
|
+
run();
|
|
33
|
+
});
|
|
34
|
+
observer.observe(element);
|
|
35
|
+
requestAnimationFrame(run);
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
cancelled = true;
|
|
39
|
+
observer?.disconnect();
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DataChannel } from './data-channel';
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_PROPS_ATTRIBUTE = 'data-props';
|
|
4
|
+
export const DEFAULT_DATA_REF_ATTRIBUTE = 'data-ref';
|
|
5
|
+
|
|
6
|
+
export type ParsePropsOptions = {
|
|
7
|
+
observedAttributes?: readonly string[];
|
|
8
|
+
propsAttribute?: string;
|
|
9
|
+
dataRefAttribute?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ParsedProps = Record<string, unknown>;
|
|
13
|
+
|
|
14
|
+
function parseJsonAttribute(
|
|
15
|
+
element: HTMLElement,
|
|
16
|
+
attributeName: string,
|
|
17
|
+
): ParsedProps | undefined {
|
|
18
|
+
const raw = element.getAttribute(attributeName);
|
|
19
|
+
if (raw === null) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const parsed: unknown = JSON.parse(raw);
|
|
24
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
25
|
+
return parsed as ParsedProps;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
console.warn(
|
|
29
|
+
`[dr-runtime] Invalid JSON in ${attributeName} on <${element.tagName.toLowerCase()}>`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function mergeChannelPayload(
|
|
36
|
+
props: ParsedProps,
|
|
37
|
+
payload: unknown,
|
|
38
|
+
): ParsedProps {
|
|
39
|
+
if (payload === undefined) {
|
|
40
|
+
return props;
|
|
41
|
+
}
|
|
42
|
+
if (
|
|
43
|
+
typeof payload === 'object' &&
|
|
44
|
+
payload !== null &&
|
|
45
|
+
!Array.isArray(payload)
|
|
46
|
+
) {
|
|
47
|
+
return { ...props, ...(payload as ParsedProps) };
|
|
48
|
+
}
|
|
49
|
+
return { ...props, data: payload };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function parseElementProps(
|
|
53
|
+
element: HTMLElement,
|
|
54
|
+
dataChannel: DataChannel,
|
|
55
|
+
options: ParsePropsOptions = {},
|
|
56
|
+
): ParsedProps {
|
|
57
|
+
const props: ParsedProps = {};
|
|
58
|
+
const observedAttributes = options.observedAttributes ?? [];
|
|
59
|
+
|
|
60
|
+
for (const name of observedAttributes) {
|
|
61
|
+
props[name] = element.getAttribute(name) ?? undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const propsAttribute = options.propsAttribute ?? DEFAULT_PROPS_ATTRIBUTE;
|
|
65
|
+
const jsonProps = parseJsonAttribute(element, propsAttribute);
|
|
66
|
+
if (jsonProps) {
|
|
67
|
+
Object.assign(props, jsonProps);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const dataRefAttribute = options.dataRefAttribute ?? DEFAULT_DATA_REF_ATTRIBUTE;
|
|
71
|
+
const dataRef = element.getAttribute(dataRefAttribute);
|
|
72
|
+
if (dataRef) {
|
|
73
|
+
props.__dataRef = dataRef;
|
|
74
|
+
return mergeChannelPayload(props, dataChannel.get(dataRef));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return props;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function readDataRef(
|
|
81
|
+
element: HTMLElement,
|
|
82
|
+
dataRefAttribute: string = DEFAULT_DATA_REF_ATTRIBUTE,
|
|
83
|
+
): string | undefined {
|
|
84
|
+
return element.getAttribute(dataRefAttribute) ?? undefined;
|
|
85
|
+
}
|