@panphora/bevel 0.1.0
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.
- package/LICENSE +16 -0
- package/README.md +226 -0
- package/bevel-fonts.css +16 -0
- package/bevel.css +3075 -0
- package/bevel.js +2126 -0
- package/dist/react/components/activity-feed.d.ts +89 -0
- package/dist/react/components/activity-feed.js +54 -0
- package/dist/react/components/alert-dialog.d.ts +14 -0
- package/dist/react/components/alert-dialog.js +19 -0
- package/dist/react/components/app-shell.d.ts +98 -0
- package/dist/react/components/app-shell.js +86 -0
- package/dist/react/components/app-toolbar.d.ts +19 -0
- package/dist/react/components/app-toolbar.js +22 -0
- package/dist/react/components/avatar-group.d.ts +67 -0
- package/dist/react/components/avatar-group.js +49 -0
- package/dist/react/components/badge-and-pill.d.ts +34 -0
- package/dist/react/components/badge-and-pill.js +12 -0
- package/dist/react/components/breadcrumbs.d.ts +56 -0
- package/dist/react/components/breadcrumbs.js +44 -0
- package/dist/react/components/bulk-bar.d.ts +22 -0
- package/dist/react/components/bulk-bar.js +59 -0
- package/dist/react/components/button.d.ts +15 -0
- package/dist/react/components/button.js +20 -0
- package/dist/react/components/callout-and-hint.d.ts +17 -0
- package/dist/react/components/callout-and-hint.js +8 -0
- package/dist/react/components/checkbox.d.ts +5 -0
- package/dist/react/components/checkbox.js +13 -0
- package/dist/react/components/chip.d.ts +149 -0
- package/dist/react/components/chip.js +29 -0
- package/dist/react/components/code-block.d.ts +50 -0
- package/dist/react/components/code-block.js +77 -0
- package/dist/react/components/code-editor.d.ts +122 -0
- package/dist/react/components/code-editor.js +695 -0
- package/dist/react/components/column-manager.d.ts +28 -0
- package/dist/react/components/column-manager.js +85 -0
- package/dist/react/components/combobox.d.ts +32 -0
- package/dist/react/components/combobox.js +37 -0
- package/dist/react/components/data-grid.d.ts +108 -0
- package/dist/react/components/data-grid.js +159 -0
- package/dist/react/components/data-table.d.ts +2 -0
- package/dist/react/components/data-table.js +6 -0
- package/dist/react/components/date.d.ts +23 -0
- package/dist/react/components/date.js +11 -0
- package/dist/react/components/details.d.ts +13 -0
- package/dist/react/components/details.js +20 -0
- package/dist/react/components/dialog.d.ts +74 -0
- package/dist/react/components/dialog.js +31 -0
- package/dist/react/components/diff-view.d.ts +73 -0
- package/dist/react/components/diff-view.js +68 -0
- package/dist/react/components/documents.d.ts +70 -0
- package/dist/react/components/documents.js +10 -0
- package/dist/react/components/drawer.d.ts +45 -0
- package/dist/react/components/drawer.js +35 -0
- package/dist/react/components/editable-grid-cells.d.ts +32 -0
- package/dist/react/components/editable-grid-cells.js +116 -0
- package/dist/react/components/eyebrow-kicker-lede-pull-quote.d.ts +39 -0
- package/dist/react/components/eyebrow-kicker-lede-pull-quote.js +12 -0
- package/dist/react/components/field.d.ts +32 -0
- package/dist/react/components/field.js +27 -0
- package/dist/react/components/file-tree.d.ts +75 -0
- package/dist/react/components/file-tree.js +247 -0
- package/dist/react/components/file.d.ts +43 -0
- package/dist/react/components/file.js +73 -0
- package/dist/react/components/files-table.d.ts +174 -0
- package/dist/react/components/files-table.js +237 -0
- package/dist/react/components/filter-builder.d.ts +37 -0
- package/dist/react/components/filter-builder.js +109 -0
- package/dist/react/components/form-error-summary.d.ts +16 -0
- package/dist/react/components/form-error-summary.js +49 -0
- package/dist/react/components/icon-button.d.ts +8 -0
- package/dist/react/components/icon-button.js +14 -0
- package/dist/react/components/icon.d.ts +7 -0
- package/dist/react/components/icon.js +15 -0
- package/dist/react/components/image.d.ts +72 -0
- package/dist/react/components/image.js +24 -0
- package/dist/react/components/inline-edit.d.ts +70 -0
- package/dist/react/components/inline-edit.js +149 -0
- package/dist/react/components/input.d.ts +29 -0
- package/dist/react/components/input.js +20 -0
- package/dist/react/components/kanban-board.d.ts +140 -0
- package/dist/react/components/kanban-board.js +374 -0
- package/dist/react/components/layout.d.ts +51 -0
- package/dist/react/components/layout.js +28 -0
- package/dist/react/components/list.d.ts +28 -0
- package/dist/react/components/list.js +6 -0
- package/dist/react/components/masthead-and-verdict.d.ts +60 -0
- package/dist/react/components/masthead-and-verdict.js +8 -0
- package/dist/react/components/menu.d.ts +26 -0
- package/dist/react/components/menu.js +31 -0
- package/dist/react/components/multi-select-picker.d.ts +49 -0
- package/dist/react/components/multi-select-picker.js +20 -0
- package/dist/react/components/note.d.ts +33 -0
- package/dist/react/components/note.js +8 -0
- package/dist/react/components/number-field.d.ts +16 -0
- package/dist/react/components/number-field.js +17 -0
- package/dist/react/components/pagination.d.ts +45 -0
- package/dist/react/components/pagination.js +87 -0
- package/dist/react/components/persistent-action-banner.d.ts +71 -0
- package/dist/react/components/persistent-action-banner.js +39 -0
- package/dist/react/components/popconfirm.d.ts +42 -0
- package/dist/react/components/popconfirm.js +59 -0
- package/dist/react/components/popover.d.ts +16 -0
- package/dist/react/components/popover.js +21 -0
- package/dist/react/components/progress-and-gauge.d.ts +44 -0
- package/dist/react/components/progress-and-gauge.js +19 -0
- package/dist/react/components/radio.d.ts +7 -0
- package/dist/react/components/radio.js +13 -0
- package/dist/react/components/rich-text-toolbar.d.ts +26 -0
- package/dist/react/components/rich-text-toolbar.js +64 -0
- package/dist/react/components/rows-and-card.d.ts +88 -0
- package/dist/react/components/rows-and-card.js +10 -0
- package/dist/react/components/save-status-indicator.d.ts +11 -0
- package/dist/react/components/save-status-indicator.js +33 -0
- package/dist/react/components/segmented-control.d.ts +21 -0
- package/dist/react/components/segmented-control.js +15 -0
- package/dist/react/components/select.d.ts +29 -0
- package/dist/react/components/select.js +29 -0
- package/dist/react/components/selectable-cards.d.ts +30 -0
- package/dist/react/components/selectable-cards.js +29 -0
- package/dist/react/components/settings-rows.d.ts +51 -0
- package/dist/react/components/settings-rows.js +8 -0
- package/dist/react/components/sidebar-nav.d.ts +77 -0
- package/dist/react/components/sidebar-nav.js +41 -0
- package/dist/react/components/skeleton-and-empty.d.ts +51 -0
- package/dist/react/components/skeleton-and-empty.js +14 -0
- package/dist/react/components/slider.d.ts +19 -0
- package/dist/react/components/slider.js +17 -0
- package/dist/react/components/small-charts.d.ts +60 -0
- package/dist/react/components/small-charts.js +76 -0
- package/dist/react/components/sortable.d.ts +30 -0
- package/dist/react/components/sortable.js +85 -0
- package/dist/react/components/split-button.d.ts +38 -0
- package/dist/react/components/split-button.js +43 -0
- package/dist/react/components/split-panes.d.ts +35 -0
- package/dist/react/components/split-panes.js +102 -0
- package/dist/react/components/stat-tiles.d.ts +54 -0
- package/dist/react/components/stat-tiles.js +8 -0
- package/dist/react/components/steps.d.ts +11 -0
- package/dist/react/components/steps.js +13 -0
- package/dist/react/components/switch.d.ts +6 -0
- package/dist/react/components/switch.js +13 -0
- package/dist/react/components/table.d.ts +61 -0
- package/dist/react/components/table.js +6 -0
- package/dist/react/components/tabs.d.ts +7 -0
- package/dist/react/components/tabs.js +13 -0
- package/dist/react/components/tags.d.ts +33 -0
- package/dist/react/components/tags.js +83 -0
- package/dist/react/components/textarea.d.ts +10 -0
- package/dist/react/components/textarea.js +6 -0
- package/dist/react/components/toast.d.ts +35 -0
- package/dist/react/components/toast.js +90 -0
- package/dist/react/components/tooltip-and-key.d.ts +20 -0
- package/dist/react/components/tooltip-and-key.js +20 -0
- package/dist/react/components/tree-view.d.ts +34 -0
- package/dist/react/components/tree-view.js +118 -0
- package/dist/react/components/upload-dropzone.d.ts +92 -0
- package/dist/react/components/upload-dropzone.js +116 -0
- package/dist/react/components/wizard-shell.d.ts +78 -0
- package/dist/react/components/wizard-shell.js +176 -0
- package/dist/react/fonts.css.d.ts +1 -0
- package/dist/react/index.d.ts +76 -0
- package/dist/react/index.js +77 -0
- package/dist/react/integrations/quickcrop.d.ts +58 -0
- package/dist/react/integrations/quickcrop.js +137 -0
- package/dist/react/integrations/richclay.d.ts +43 -0
- package/dist/react/integrations/richclay.js +129 -0
- package/dist/react/internal/classes.d.ts +3 -0
- package/dist/react/internal/classes.js +10 -0
- package/dist/react/internal/close-mark.d.ts +1 -0
- package/dist/react/internal/close-mark.js +10 -0
- package/dist/react/internal/controllable-state.d.ts +9 -0
- package/dist/react/internal/controllable-state.js +22 -0
- package/dist/react/internal/drop-effect.d.ts +28 -0
- package/dist/react/internal/drop-effect.js +8 -0
- package/dist/react/internal/form-reset.d.ts +2 -0
- package/dist/react/internal/form-reset.js +22 -0
- package/dist/react/internal/format.d.ts +1 -0
- package/dist/react/internal/format.js +14 -0
- package/dist/react/internal/link.d.ts +2 -0
- package/dist/react/internal/link.js +8 -0
- package/dist/react/internal/part.d.ts +20 -0
- package/dist/react/internal/part.js +47 -0
- package/dist/react/internal/pointer-session.d.ts +8 -0
- package/dist/react/internal/pointer-session.js +60 -0
- package/dist/react/internal/refs.d.ts +2 -0
- package/dist/react/internal/refs.js +22 -0
- package/dist/react/internal/rich-text.d.ts +28 -0
- package/dist/react/internal/rich-text.js +4 -0
- package/dist/react/internal/table-context.d.ts +39 -0
- package/dist/react/internal/table-context.js +12 -0
- package/dist/react/internal/use-object-url.d.ts +1 -0
- package/dist/react/internal/use-object-url.js +15 -0
- package/dist/react/layout.css +274 -0
- package/dist/react/layout.css.d.ts +1 -0
- package/dist/react/models/filter.d.ts +31 -0
- package/dist/react/models/filter.js +79 -0
- package/dist/react/models/kanban.d.ts +51 -0
- package/dist/react/models/kanban.js +35 -0
- package/dist/react/models/reorder.d.ts +9 -0
- package/dist/react/models/reorder.js +23 -0
- package/dist/react/models/table.d.ts +85 -0
- package/dist/react/models/table.js +144 -0
- package/dist/react/models/tree.d.ts +16 -0
- package/dist/react/models/tree.js +26 -0
- package/dist/react/provider.d.ts +18 -0
- package/dist/react/provider.js +31 -0
- package/dist/react/recipes/context-hypercms.d.ts +17 -0
- package/dist/react/recipes/context-hypercms.js +71 -0
- package/dist/react/recipes/context-quickcrop.d.ts +8 -0
- package/dist/react/recipes/context-quickcrop.js +49 -0
- package/dist/react/theme.css +8004 -0
- package/dist/react/theme.css.d.ts +1 -0
- package/examples/README.md +34 -0
- package/examples/components/activity-feed.html +28 -0
- package/examples/components/app-shell.html +12 -0
- package/examples/components/app-toolbar.html +38 -0
- package/examples/components/avatar-group.html +27 -0
- package/examples/components/breadcrumbs.html +26 -0
- package/examples/components/bulk-bar.html +22 -0
- package/examples/components/code-block.html +12 -0
- package/examples/components/code-editor.html +25 -0
- package/examples/components/column-manager.html +9 -0
- package/examples/components/combobox.html +13 -0
- package/examples/components/context-hypercms.css +41 -0
- package/examples/components/context-hypercms.html +40 -0
- package/examples/components/context-hypercms.js +62 -0
- package/examples/components/context-quickcrop.html +137 -0
- package/examples/components/diff-view.html +9 -0
- package/examples/components/drawer.html +11 -0
- package/examples/components/editable-grid-cells.html +9 -0
- package/examples/components/file-browser.html +53 -0
- package/examples/components/file-tree.html +34 -0
- package/examples/components/files-table.html +80 -0
- package/examples/components/filter-builder.html +11 -0
- package/examples/components/form-error-summary.html +11 -0
- package/examples/components/index.html +51 -0
- package/examples/components/inline-edit.html +42 -0
- package/examples/components/kanban-board.html +42 -0
- package/examples/components/manifest.json +3279 -0
- package/examples/components/modal.html +11 -0
- package/examples/components/multi-select-picker.html +13 -0
- package/examples/components/pagination.html +32 -0
- package/examples/components/persistent-action-banner.html +23 -0
- package/examples/components/popconfirm.html +13 -0
- package/examples/components/popover-menu.html +13 -0
- package/examples/components/reference/baseline/DECISIONS.md +85 -0
- package/examples/components/reference/baseline/README.md +58 -0
- package/examples/components/reference/baseline/bevel-fonts.css +16 -0
- package/examples/components/reference/baseline/bevel.css +1339 -0
- package/examples/components/reference/baseline/bevel.js +496 -0
- package/examples/components/reference/baseline/fonts/LICENSE-ibm-plex-mono.txt +93 -0
- package/examples/components/reference/baseline/fonts/LICENSE-ibm-plex-sans.txt +93 -0
- package/examples/components/reference/baseline/fonts/LICENSE-newsreader.txt +93 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
- package/examples/components/reference/baseline/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
- package/examples/components/reference/fable/IMPROVEMENTS.md +106 -0
- package/examples/components/reference/fable/PARENT-REPAIRS.md +17 -0
- package/examples/components/reference/fable/__pycache__/build_repairs.cpython-311.pyc +0 -0
- package/examples/components/reference/fable/__pycache__/kanban.cpython-311.pyc +0 -0
- package/examples/components/reference/fable/as-authored/IMPROVEMENTS-completed.md +106 -0
- package/examples/components/reference/fable/as-authored/base-astra.css +17 -0
- package/examples/components/reference/fable/as-authored/base-parent.css +416 -0
- package/examples/components/reference/fable/as-authored/build.py +858 -0
- package/examples/components/reference/fable/as-authored/fable.css +823 -0
- package/examples/components/reference/fable/as-authored/kanban-completed.css +235 -0
- package/examples/components/reference/fable/as-authored/kanban-completed.html +53 -0
- package/examples/components/reference/fable/as-authored/kanban-completed.py +212 -0
- package/examples/components/reference/fable/as-authored/kanban.py +207 -0
- package/examples/components/reference/fable/base-astra.css +17 -0
- package/examples/components/reference/fable/base-parent.css +416 -0
- package/examples/components/reference/fable/build.py +858 -0
- package/examples/components/reference/fable/build_repairs.py +25 -0
- package/examples/components/reference/fable/components/activity-feed.html +3 -0
- package/examples/components/reference/fable/components/app-toolbar.html +3 -0
- package/examples/components/reference/fable/components/avatar-group.html +4 -0
- package/examples/components/reference/fable/components/breadcrumbs.html +3 -0
- package/examples/components/reference/fable/components/code-block.html +9 -0
- package/examples/components/reference/fable/components/column-manager.html +3 -0
- package/examples/components/reference/fable/components/combobox.html +6 -0
- package/examples/components/reference/fable/components/context-hypercms.html +3 -0
- package/examples/components/reference/fable/components/context-quickcrop.html +4 -0
- package/examples/components/reference/fable/components/diff-view.html +13 -0
- package/examples/components/reference/fable/components/drawer.html +3 -0
- package/examples/components/reference/fable/components/editable-grid-cells.html +3 -0
- package/examples/components/reference/fable/components/filter-builder.html +3 -0
- package/examples/components/reference/fable/components/form-error-summary.html +4 -0
- package/examples/components/reference/fable/components/inline-edit.html +3 -0
- package/examples/components/reference/fable/components/kanban-board.html +53 -0
- package/examples/components/reference/fable/components/modal.html +3 -0
- package/examples/components/reference/fable/components/multi-select-picker.html +5 -0
- package/examples/components/reference/fable/components/pagination.html +3 -0
- package/examples/components/reference/fable/components/persistent-action-banner.html +4 -0
- package/examples/components/reference/fable/components/popconfirm.html +5 -0
- package/examples/components/reference/fable/components/popover-menu.html +3 -0
- package/examples/components/reference/fable/components/rich-text-toolbar.html +3 -0
- package/examples/components/reference/fable/components/save-status-indicator.html +2 -0
- package/examples/components/reference/fable/components/selectable-cards.html +3 -0
- package/examples/components/reference/fable/components/settings-rows.html +3 -0
- package/examples/components/reference/fable/components/sidebar-nav.html +4 -0
- package/examples/components/reference/fable/components/small-charts.html +5 -0
- package/examples/components/reference/fable/components/split-button.html +4 -0
- package/examples/components/reference/fable/components/split-panes.html +8 -0
- package/examples/components/reference/fable/components/stat-tiles.html +3 -0
- package/examples/components/reference/fable/components/toast-stack.html +5 -0
- package/examples/components/reference/fable/components/tree-view.html +4 -0
- package/examples/components/reference/fable/components/upload-dropzone.html +6 -0
- package/examples/components/reference/fable/components/wizard-shell.html +3 -0
- package/examples/components/reference/fable/coverage.json +414 -0
- package/examples/components/reference/fable/fable.css +823 -0
- package/examples/components/reference/fable/index.html +5 -0
- package/examples/components/reference/fable/kanban.css +237 -0
- package/examples/components/reference/fable/kanban.py +212 -0
- package/examples/components/reference/fable/parent-repairs.css +9 -0
- package/examples/components/reference/fixtures/data.json +102 -0
- package/examples/components/reference/fixtures/host.js +47 -0
- package/examples/components/reference/fixtures/static-host.js +39 -0
- package/examples/components/reference/fixtures/workspace.jpg +0 -0
- package/examples/components/reference/parent/CHANGES.md +179 -0
- package/examples/components/reference/parent/__pycache__/kanban.cpython-311.pyc +0 -0
- package/examples/components/reference/parent/base-astra.css +17 -0
- package/examples/components/reference/parent/base-parent.css +416 -0
- package/examples/components/reference/parent/build.py +148 -0
- package/examples/components/reference/parent/components/activity-feed.html +2 -0
- package/examples/components/reference/parent/components/app-toolbar.html +2 -0
- package/examples/components/reference/parent/components/avatar-group.html +4 -0
- package/examples/components/reference/parent/components/breadcrumbs.html +2 -0
- package/examples/components/reference/parent/components/code-block.html +8 -0
- package/examples/components/reference/parent/components/column-manager.html +2 -0
- package/examples/components/reference/parent/components/combobox.html +6 -0
- package/examples/components/reference/parent/components/context-hypercms.html +2 -0
- package/examples/components/reference/parent/components/context-quickcrop.html +4 -0
- package/examples/components/reference/parent/components/diff-view.html +4 -0
- package/examples/components/reference/parent/components/drawer.html +2 -0
- package/examples/components/reference/parent/components/editable-grid-cells.html +2 -0
- package/examples/components/reference/parent/components/filter-builder.html +2 -0
- package/examples/components/reference/parent/components/form-error-summary.html +2 -0
- package/examples/components/reference/parent/components/inline-edit.html +2 -0
- package/examples/components/reference/parent/components/kanban-board.html +84 -0
- package/examples/components/reference/parent/components/modal.html +2 -0
- package/examples/components/reference/parent/components/multi-select-picker.html +5 -0
- package/examples/components/reference/parent/components/pagination.html +2 -0
- package/examples/components/reference/parent/components/persistent-action-banner.html +4 -0
- package/examples/components/reference/parent/components/popconfirm.html +5 -0
- package/examples/components/reference/parent/components/popover-menu.html +2 -0
- package/examples/components/reference/parent/components/rich-text-toolbar.html +2 -0
- package/examples/components/reference/parent/components/save-status-indicator.html +2 -0
- package/examples/components/reference/parent/components/selectable-cards.html +2 -0
- package/examples/components/reference/parent/components/settings-rows.html +2 -0
- package/examples/components/reference/parent/components/sidebar-nav.html +4 -0
- package/examples/components/reference/parent/components/small-charts.html +2 -0
- package/examples/components/reference/parent/components/split-button.html +5 -0
- package/examples/components/reference/parent/components/split-panes.html +8 -0
- package/examples/components/reference/parent/components/stat-tiles.html +2 -0
- package/examples/components/reference/parent/components/toast-stack.html +5 -0
- package/examples/components/reference/parent/components/tree-view.html +4 -0
- package/examples/components/reference/parent/components/upload-dropzone.html +6 -0
- package/examples/components/reference/parent/components/wizard-shell.html +2 -0
- package/examples/components/reference/parent/coverage.json +414 -0
- package/examples/components/reference/parent/index.html +1 -0
- package/examples/components/reference/parent/kanban.css +114 -0
- package/examples/components/reference/parent/kanban.py +55 -0
- package/examples/components/reference/parent/refinement.css +53 -0
- package/examples/components/reference/selected/build.py +70 -0
- package/examples/components/reference/selected/components/activity-feed.html +3 -0
- package/examples/components/reference/selected/components/app-toolbar.html +3 -0
- package/examples/components/reference/selected/components/avatar-group.html +4 -0
- package/examples/components/reference/selected/components/breadcrumbs.html +3 -0
- package/examples/components/reference/selected/components/code-block.html +9 -0
- package/examples/components/reference/selected/components/column-manager.html +3 -0
- package/examples/components/reference/selected/components/combobox.html +6 -0
- package/examples/components/reference/selected/components/context-hypercms.html +3 -0
- package/examples/components/reference/selected/components/context-quickcrop.html +4 -0
- package/examples/components/reference/selected/components/diff-view.html +13 -0
- package/examples/components/reference/selected/components/drawer.html +3 -0
- package/examples/components/reference/selected/components/editable-grid-cells.html +3 -0
- package/examples/components/reference/selected/components/filter-builder.html +3 -0
- package/examples/components/reference/selected/components/form-error-summary.html +4 -0
- package/examples/components/reference/selected/components/inline-edit.html +3 -0
- package/examples/components/reference/selected/components/kanban-board.html +53 -0
- package/examples/components/reference/selected/components/modal.html +2 -0
- package/examples/components/reference/selected/components/multi-select-picker.html +5 -0
- package/examples/components/reference/selected/components/pagination.html +3 -0
- package/examples/components/reference/selected/components/persistent-action-banner.html +4 -0
- package/examples/components/reference/selected/components/popconfirm.html +5 -0
- package/examples/components/reference/selected/components/popover-menu.html +3 -0
- package/examples/components/reference/selected/components/rich-text-toolbar.html +3 -0
- package/examples/components/reference/selected/components/save-status-indicator.html +2 -0
- package/examples/components/reference/selected/components/selectable-cards.html +3 -0
- package/examples/components/reference/selected/components/settings-rows.html +3 -0
- package/examples/components/reference/selected/components/sidebar-nav.html +4 -0
- package/examples/components/reference/selected/components/small-charts.html +5 -0
- package/examples/components/reference/selected/components/split-button.html +4 -0
- package/examples/components/reference/selected/components/split-panes.html +8 -0
- package/examples/components/reference/selected/components/stat-tiles.html +3 -0
- package/examples/components/reference/selected/components/toast-stack.html +5 -0
- package/examples/components/reference/selected/components/tree-view.html +4 -0
- package/examples/components/reference/selected/components/upload-dropzone.html +6 -0
- package/examples/components/reference/selected/components/wizard-shell.html +3 -0
- package/examples/components/reference/selected/corrections.css +11 -0
- package/examples/components/reference/selected/coverage.json +413 -0
- package/examples/components/reference/selected/gallery-build.mjs +49 -0
- package/examples/components/reference/selected/gallery-spec.json +12 -0
- package/examples/components/reference/selected/index.html +1 -0
- package/examples/components/reference/selected/kanban-comparison.json +6 -0
- package/examples/components/reference/selected/manifest.json +476 -0
- package/examples/components/reference/sha256.json +142 -0
- package/examples/components/reference/sources/diagonal-close.svg +1 -0
- package/examples/components/reference/sources/hyperclay/public-assets/css/hyperclay-app-source.css +614 -0
- package/examples/components/reference/sources/hyperclay/public-assets/js/app.js +94 -0
- package/examples/components/reference/sources/hyperclayjs/src/ui/theModal.js +897 -0
- package/examples/components/reference/sources/hypercms/scripts/build-theme.js +203 -0
- package/examples/components/reference/sources/hypercms/src/anchor.js +24 -0
- package/examples/components/reference/sources/hypercms/src/apply-loop.js +167 -0
- package/examples/components/reference/sources/hypercms/src/diagnostics.js +44 -0
- package/examples/components/reference/sources/hypercms/src/enhance.js +173 -0
- package/examples/components/reference/sources/hypercms/src/events.js +955 -0
- package/examples/components/reference/sources/hypercms/src/form-builder.js +518 -0
- package/examples/components/reference/sources/hypercms/src/form-rules.js +208 -0
- package/examples/components/reference/sources/hypercms/src/hooks.js +100 -0
- package/examples/components/reference/sources/hypercms/src/hypercms-bundle.js +10 -0
- package/examples/components/reference/sources/hypercms/src/hypercms.js +347 -0
- package/examples/components/reference/sources/hypercms/src/morph.js +10 -0
- package/examples/components/reference/sources/hypercms/src/path.js +61 -0
- package/examples/components/reference/sources/hypercms/src/place.js +131 -0
- package/examples/components/reference/sources/hypercms/src/platform.js +131 -0
- package/examples/components/reference/sources/hypercms/src/refresh.js +87 -0
- package/examples/components/reference/sources/hypercms/src/richclay-bridge.js +140 -0
- package/examples/components/reference/sources/hypercms/src/row-identity.js +111 -0
- package/examples/components/reference/sources/hypercms/src/scaffold.js +10 -0
- package/examples/components/reference/sources/hypercms/src/session.js +303 -0
- package/examples/components/reference/sources/hypercms/src/shell.js +248 -0
- package/examples/components/reference/sources/hypercms/src/styles.css +298 -0
- package/examples/components/reference/sources/hypercms/src/targets.js +144 -0
- package/examples/components/reference/sources/hypercms/src/templates.js +656 -0
- package/examples/components/reference/sources/hypercms/src/theme/pixel-quiet.overrides.css +761 -0
- package/examples/components/reference/sources/hypercms/src/theme.generated.css +1772 -0
- package/examples/components/reference/sources/hypercms/src/toggle.js +666 -0
- package/examples/components/reference/sources/hypercms/src/unresolved.js +211 -0
- package/examples/components/reference/sources/hypercms/src/vendor/mirk.vendor.js +212 -0
- package/examples/components/reference/sources/hypercms/src/views/inline-layer.js +421 -0
- package/examples/components/reference/sources/hypercms/src/views/inline.js +1072 -0
- package/examples/components/reference/sources/hypercms/src/views/sidebar.js +97 -0
- package/examples/components/reference/sources/modal-reference.html +301 -0
- package/examples/components/reference/sources/quickcrop/quickcrop.css +124 -0
- package/examples/components/reference/sources/quickcrop/quickcrop.js +400 -0
- package/examples/components/reference/sources/richclay/richclay.css +416 -0
- package/examples/components/reference/sources/richclay/richclay.js +2962 -0
- package/examples/components/reference/sources/richclay/src/a11y.js +67 -0
- package/examples/components/reference/sources/richclay/src/browser-global.js +7 -0
- package/examples/components/reference/sources/richclay/src/buttons.js +254 -0
- package/examples/components/reference/sources/richclay/src/hyperclay.js +470 -0
- package/examples/components/reference/sources/richclay/src/normalize.js +380 -0
- package/examples/components/reference/sources/richclay/src/richclay.js +1356 -0
- package/examples/components/reference/sources/richclay/src/sanitize.js +189 -0
- package/examples/components/reference/sources/richclay/src/styles.js +23 -0
- package/examples/components/reference/sources/richclay/src/toolbar-float.js +175 -0
- package/examples/components/reference/sources/richclay/src/toolbar.js +394 -0
- package/examples/components/reference/sources/richclay/src/vendor-bundle.js +15 -0
- package/examples/components/reference/sources/richclay/vendor/DOMPURIFY-LICENSE +202 -0
- package/examples/components/reference/sources/richclay/vendor/DOMPURIFY-LICENSE-MPL +373 -0
- package/examples/components/reference/sources/richclay/vendor/SQUIRE-LICENSE +21 -0
- package/examples/components/reference/sources/richclay/vendor/purify.es.mjs +2249 -0
- package/examples/components/reference/sources/richclay/vendor/purify.min.js +3 -0
- package/examples/components/reference/sources/richclay/vendor/squire.js +22 -0
- package/examples/components/reference/sources/richclay/vendor/squire.mjs +22 -0
- package/examples/components/reference/sources/richclay/vendor/squire.provenance.json +19 -0
- package/examples/components/rich-text-toolbar.html +43 -0
- package/examples/components/save-status-indicator.html +11 -0
- package/examples/components/selectable-cards.html +9 -0
- package/examples/components/settings-rows.html +10 -0
- package/examples/components/sidebar-nav.html +9 -0
- package/examples/components/small-charts.html +21 -0
- package/examples/components/split-button.html +13 -0
- package/examples/components/split-panes-editor.html +6 -0
- package/examples/components/split-panes.html +9 -0
- package/examples/components/stat-tiles.html +23 -0
- package/examples/components/toast-stack.html +21 -0
- package/examples/components/tree-view.html +9 -0
- package/examples/components/upload-dropzone.html +88 -0
- package/examples/components/wizard-shell.html +50 -0
- package/examples/datastar/index.html +38 -0
- package/examples/datastar/server.mjs +31 -0
- package/examples/fixtures/field-guide.pdf +14 -0
- package/examples/fixtures/workspace.jpg +0 -0
- package/examples/setup-peers.mjs +35 -0
- package/fonts/LICENSE-ibm-plex-mono.txt +93 -0
- package/fonts/LICENSE-ibm-plex-sans.txt +93 -0
- package/fonts/LICENSE-newsreader.txt +93 -0
- package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- package/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
- package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
- package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
- package/icons/LICENSE +21 -0
- package/icons/house.json +71 -0
- package/icons/icons.d.ts +17 -0
- package/icons/icons.js +648 -0
- package/icons/icons.svg +126 -0
- package/icons/marks/build-tray.source.mjs +45 -0
- package/icons/marks/hyperclay-local.svg +11 -0
- package/icons/phosphor.json +53 -0
- package/icons/round.json +8 -0
- package/integrations/datastar.js +202 -0
- package/integrations/quickcrop.css +26 -0
- package/integrations/quickcrop.js +56 -0
- package/integrations/richclay.css +100 -0
- package/integrations/richclay.js +131 -0
- package/package.json +630 -0
- package/react/README.md +244 -0
- package/src/README.md +83 -0
- package/vendor/datastar/datastar-1.0.3.js +10 -0
- package/vendor/datastar/provenance.json +6 -0
- package/vendor/floating-ui/LICENSE-core +20 -0
- package/vendor/floating-ui/LICENSE-dom +20 -0
- package/vendor/floating-ui/floating-ui.core.umd.min.js +1 -0
- package/vendor/floating-ui/floating-ui.dom.umd.min.js +1 -0
- package/vendor/floating-ui/provenance.json +17 -0
- package/vendor/richclay/LICENSE +16 -0
- package/vendor/richclay/THIRD-PARTY-NOTICES.md +45 -0
- package/vendor/richclay/provenance.json +17 -0
- package/vendor/richclay/richclay.css +404 -0
- package/vendor/richclay/richclay.js +2940 -0
- package/vendor/richclay/vendor/DOMPURIFY-LICENSE +202 -0
- package/vendor/richclay/vendor/DOMPURIFY-LICENSE-MPL +373 -0
- package/vendor/richclay/vendor/SQUIRE-LICENSE +21 -0
- package/vendor/richclay/vendor/purify.min.js +3 -0
- package/vendor/richclay/vendor/squire.js +22 -0
- package/vendor/richclay/vendor/squire.provenance.json +30 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shapeKindOf,
|
|
3
|
+
findTemplate,
|
|
4
|
+
isInlineTemplate,
|
|
5
|
+
componentForScalarRule,
|
|
6
|
+
winningScalarArrayComponent,
|
|
7
|
+
} from './templates.js'
|
|
8
|
+
|
|
9
|
+
const TAG_PROP_MAP = {
|
|
10
|
+
IMG: 'src',
|
|
11
|
+
A: 'href',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function fieldPropertyFor(el) {
|
|
15
|
+
if (!el) return 'value'
|
|
16
|
+
const tag = (el.tagName || '').toUpperCase()
|
|
17
|
+
if (tag === 'INPUT') {
|
|
18
|
+
const type = (el.getAttribute('type') || 'text').toLowerCase()
|
|
19
|
+
if (type === 'checkbox') return 'checked'
|
|
20
|
+
return 'value'
|
|
21
|
+
}
|
|
22
|
+
if (tag === 'TEXTAREA' || tag === 'SELECT') return 'value'
|
|
23
|
+
if (TAG_PROP_MAP[tag]) return TAG_PROP_MAP[tag]
|
|
24
|
+
// Rich-text surfaces (the @richtext component): the value IS the markup.
|
|
25
|
+
if (el.hasAttribute && el.hasAttribute('contenteditable')) return 'innerHTML'
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Tag-specific scalar field selector. Returns a selector that uniquely
|
|
30
|
+
// identifies the leaf input under data-hcms-field="key" — necessary because
|
|
31
|
+
// data-hcms-field is now stamped on every keyed node (containers + leaves),
|
|
32
|
+
// so a bare `[data-hcms-field="key"]` would match the wrapping container too.
|
|
33
|
+
export function fieldSelectorFor(el, key) {
|
|
34
|
+
const tag = (el.tagName || '').toUpperCase()
|
|
35
|
+
const type = (el.getAttribute && el.getAttribute('type') || '').toLowerCase()
|
|
36
|
+
const prop = fieldPropertyFor(el)
|
|
37
|
+
const tagSel = tagSelectorFor(tag, type)
|
|
38
|
+
const fieldSel = `${tagSel}[data-hcms-field="${cssEscape(key)}"]`
|
|
39
|
+
if (tag === 'INPUT' && type === 'radio') {
|
|
40
|
+
return `${fieldSel}:checked@value`
|
|
41
|
+
}
|
|
42
|
+
if (prop) return `${fieldSel}@${prop}`
|
|
43
|
+
return fieldSel
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Build a per-item leaf selector with no key qualifier — used by scalar-array
|
|
47
|
+
// rules where the leaf is scoped by the surrounding per-item context, so the
|
|
48
|
+
// data-hcms-field key isn't needed (and items don't have a stable key anyway).
|
|
49
|
+
function keylessFieldSelectorFor(el) {
|
|
50
|
+
const tag = (el.tagName || '').toUpperCase()
|
|
51
|
+
const type = (el.getAttribute && el.getAttribute('type') || '').toLowerCase()
|
|
52
|
+
const prop = fieldPropertyFor(el)
|
|
53
|
+
const tagSel = tagSelectorFor(tag, type)
|
|
54
|
+
const base = `${tagSel}[data-hcms-field]`
|
|
55
|
+
if (tag === 'INPUT' && type === 'radio') return `${base}:checked@value`
|
|
56
|
+
if (prop) return `${base}@${prop}`
|
|
57
|
+
return base
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function tagSelectorFor(tag, type) {
|
|
61
|
+
if (tag === 'INPUT') {
|
|
62
|
+
if (type) return `input[type="${type}"]`
|
|
63
|
+
return 'input'
|
|
64
|
+
}
|
|
65
|
+
if (tag === 'TEXTAREA') return 'textarea'
|
|
66
|
+
if (tag === 'SELECT') return 'select'
|
|
67
|
+
if (tag === 'IMG') return 'img'
|
|
68
|
+
if (tag === 'A') return 'a'
|
|
69
|
+
// Custom/unknown element (e.g. contenteditable div) — match by attribute
|
|
70
|
+
// and exclude every container shape (scalar wrapper too) so the wrapping
|
|
71
|
+
// node never shadows the inner leaf. v0.3 stamps data-hcms-field on the
|
|
72
|
+
// scalar wrapper as well, so omitting `scalar` here would otherwise match
|
|
73
|
+
// the wrapper first and extract its full text instead of the leaf's.
|
|
74
|
+
return ':not([data-hcms-shape="scalar"]):not([data-hcms-shape="object"]):not([data-hcms-shape="object-array"]):not([data-hcms-shape="scalar-array"])'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function cssEscape(value) {
|
|
78
|
+
if (typeof CSS !== 'undefined' && CSS.escape) return CSS.escape(value)
|
|
79
|
+
return String(value).replace(/[^a-zA-Z0-9_\-.*]/g, (c) => '\\' + c)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype'])
|
|
83
|
+
|
|
84
|
+
export function deriveFormRules(pageRules, doc) {
|
|
85
|
+
return derive(pageRules, [])
|
|
86
|
+
|
|
87
|
+
function derive(rule, pathArr) {
|
|
88
|
+
const kind = shapeKindOf(rule)
|
|
89
|
+
if (kind === 'scalar') return scalarRule(rule, pathArr)
|
|
90
|
+
if (kind === 'scalar-array') return scalarArrayRule(rule, pathArr)
|
|
91
|
+
if (kind === 'object-array') return objectArrayRule(rule, pathArr)
|
|
92
|
+
if (kind === 'object') {
|
|
93
|
+
const out = Object.create(null)
|
|
94
|
+
for (const [k, child] of Object.entries(rule)) {
|
|
95
|
+
if (FORBIDDEN_KEYS.has(k)) {
|
|
96
|
+
throw new Error(`hypercms: rule key "${k}" is forbidden at "${pathArr.join('.') || '<root>'}"`)
|
|
97
|
+
}
|
|
98
|
+
out[k] = derive(child, [...pathArr, k])
|
|
99
|
+
}
|
|
100
|
+
return out
|
|
101
|
+
}
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function scalarRule(rule, pathArr) {
|
|
106
|
+
const key = pathArr.length ? pathArr[pathArr.length - 1] : null
|
|
107
|
+
const fieldKey = typeof key === 'string' ? key : '__value'
|
|
108
|
+
const inlineEl = findInlineFieldEl(pathArr, fieldKey)
|
|
109
|
+
if (inlineEl) return fieldSelectorFor(inlineEl, fieldKey)
|
|
110
|
+
// No path-bound override: resolve the component this rule opts into
|
|
111
|
+
// (@scalar / @image / @file) and derive the selector from THAT template's
|
|
112
|
+
// leaf tag, so an @image field reads img@src and an @file field reads
|
|
113
|
+
// a@href, while a plain scalar still respects a global @scalar override
|
|
114
|
+
// (e.g. a <textarea>) instead of assuming <input>.
|
|
115
|
+
const shapeEl = findShapeFieldEl(componentForScalarRule(rule, doc, pathArr, pageRules), fieldKey)
|
|
116
|
+
if (shapeEl) return fieldSelectorFor(shapeEl, fieldKey)
|
|
117
|
+
return `input[data-hcms-field="${cssEscape(fieldKey)}"]@value`
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function scalarArrayRule(rule, pathArr) {
|
|
121
|
+
// The engine's "selector[]" form is text-only; for an editable form we need
|
|
122
|
+
// per-item @value reads, so emit the [selector, shape] form with a scalar
|
|
123
|
+
// shape that targets the inner field's value property. If the site
|
|
124
|
+
// overrides @scalar-array-item with a non-<input> leaf (textarea, etc.),
|
|
125
|
+
// derive the inner-leaf selector from the override. A component array
|
|
126
|
+
// (chips) derives from its own item template first — but only when the
|
|
127
|
+
// chips template actually wins at this path (winningScalarArrayComponent
|
|
128
|
+
// is the same verdict buildScalarArray uses, so the derived item selector
|
|
129
|
+
// always matches the rendered item leaf).
|
|
130
|
+
const comp = winningScalarArrayComponent(rule, pathArr, doc)
|
|
131
|
+
const shapeEl =
|
|
132
|
+
(comp && findShapeFieldEl(comp.item, null)) || findShapeFieldEl('@scalar-array-item', null)
|
|
133
|
+
const itemSel = shapeEl ? keylessFieldSelectorFor(shapeEl) : 'input[data-hcms-field]@value'
|
|
134
|
+
return [itemSelectorFor(pathArr, '[data-hcms-array-item]'), itemSel]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function objectArrayRule(rule, pathArr) {
|
|
138
|
+
const [, itemShape] = rule
|
|
139
|
+
const itemPath = [...pathArr, '*']
|
|
140
|
+
const itemSelector = itemSelectorFor(pathArr, '[data-hcms-card]')
|
|
141
|
+
if (itemShape && typeof itemShape === 'object' && !Array.isArray(itemShape)) {
|
|
142
|
+
const obj = Object.create(null)
|
|
143
|
+
for (const [k, child] of Object.entries(itemShape)) {
|
|
144
|
+
if (FORBIDDEN_KEYS.has(k)) {
|
|
145
|
+
throw new Error(`hypercms: rule key "${k}" is forbidden at "${itemPath.join('.')}"`)
|
|
146
|
+
}
|
|
147
|
+
obj[k] = derive(child, [...itemPath, k])
|
|
148
|
+
}
|
|
149
|
+
return [itemSelector, obj]
|
|
150
|
+
}
|
|
151
|
+
return [itemSelector, derive(itemShape, [...itemPath, 0])]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Build a selector that scopes item matching to the right array container.
|
|
155
|
+
// Top-level paths use exact data-hcms-path match. Nested paths use the
|
|
156
|
+
// stable data-hcms-field key (which the form-builder stamps on every keyed
|
|
157
|
+
// container) — this disambiguates sibling arrays that share a terminal key
|
|
158
|
+
// (e.g. products.*.primary.variants vs products.*.secondary.variants would
|
|
159
|
+
// both have a `variants` array with the same last segment).
|
|
160
|
+
function itemSelectorFor(pathArr, itemTag) {
|
|
161
|
+
const lastKey = pathArr.length ? pathArr[pathArr.length - 1] : ''
|
|
162
|
+
const hasWildcard = pathArr.some((s) => s === '*')
|
|
163
|
+
const pathStr = pathArr.join('.')
|
|
164
|
+
const containerSel = hasWildcard
|
|
165
|
+
? `[data-hcms-field="${cssEscape(lastKey)}"]`
|
|
166
|
+
: `[data-hcms-path="${cssEscape(pathStr)}"]`
|
|
167
|
+
return `${containerSel} > .hcms-array-items > ${itemTag}`
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Look up the field leaf inside a shape-default template (@scalar,
|
|
171
|
+
// @scalar-array-item, etc.). Sites can override these globally, so the
|
|
172
|
+
// selector emitted must reflect the actual leaf tag/type, not assume input.
|
|
173
|
+
// Returns null if the template isn't found or has no [data-hcms-field].
|
|
174
|
+
function findShapeFieldEl(shapeKey, fieldKey) {
|
|
175
|
+
if (!doc) return null
|
|
176
|
+
const tpl = findTemplate(doc, shapeKey)
|
|
177
|
+
if (!tpl) return null
|
|
178
|
+
const content = tpl.content || tpl
|
|
179
|
+
if (fieldKey) {
|
|
180
|
+
const keyed = content.querySelector(`[data-hcms-field="${cssEscape(fieldKey)}"]`)
|
|
181
|
+
if (keyed) return keyed
|
|
182
|
+
}
|
|
183
|
+
return content.querySelector('[data-hcms-field]')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function findInlineFieldEl(pathArr, fieldKey) {
|
|
187
|
+
if (!doc) return null
|
|
188
|
+
const wildcardKey = pathArr.map((s) => (typeof s === 'number' ? '*' : s)).join('.')
|
|
189
|
+
const pathStr = pathArr.join('.')
|
|
190
|
+
const candidates = [pathStr, wildcardKey]
|
|
191
|
+
for (let i = pathArr.length - 1; i >= 0; i--) {
|
|
192
|
+
const slice = pathArr.slice(0, i).map((s) => (typeof s === 'number' ? '*' : s))
|
|
193
|
+
slice.push('*')
|
|
194
|
+
candidates.push(slice.join('.'))
|
|
195
|
+
}
|
|
196
|
+
for (const key of candidates) {
|
|
197
|
+
if (!key) continue
|
|
198
|
+
const tpl = findTemplate(doc, key)
|
|
199
|
+
if (!tpl || !isInlineTemplate(tpl)) continue
|
|
200
|
+
const content = tpl.content || tpl
|
|
201
|
+
const el =
|
|
202
|
+
content.querySelector(`[data-hcms-field="${cssEscape(fieldKey)}"]`) ||
|
|
203
|
+
content.querySelector('[data-hcms-field]')
|
|
204
|
+
if (el) return el
|
|
205
|
+
}
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// The two clone hooks hypercms installs on the host client, and the retry that
|
|
2
|
+
// covers a client arriving after hypercms does.
|
|
3
|
+
//
|
|
4
|
+
// Their own module rather than private state inside hypercms.js, because the
|
|
5
|
+
// inline view has to install the snapshot hook too: by the time someone clicks
|
|
6
|
+
// a heading the client is certainly loaded, which makes the bind the reliable
|
|
7
|
+
// second trigger for a page that lost the readiness race. Importing that back
|
|
8
|
+
// out of the entry module would have made the entry module part of a cycle.
|
|
9
|
+
|
|
10
|
+
import { platform, onPlatformEvent, PLATFORM_READY } from './platform.js'
|
|
11
|
+
import { BOUND_ID_ATTR, cleanRichClayFromSnapshot, pristineMarkupFor } from './richclay-bridge.js'
|
|
12
|
+
import { SESSION_OPEN_CLASS } from './shell.js'
|
|
13
|
+
|
|
14
|
+
// Strip hypercms's own body chrome from the SAVE clone so it never reaches disk
|
|
15
|
+
// (the shell element itself is [save-remove], but the hcms-open class lives on
|
|
16
|
+
// <body>, outside the stripped subtree). Registered once at first open():
|
|
17
|
+
// onPrepareForSave runs save-only, on a clone, so it never touches the live DOM,
|
|
18
|
+
// and removing absent classes is a no-op when the shell is closed. Guarded so it
|
|
19
|
+
// degrades cleanly when the host save pipeline (hyperclayjs) isn't present.
|
|
20
|
+
let prepareHookInstalled = false
|
|
21
|
+
export function installSavePrepareHook() {
|
|
22
|
+
if (prepareHookInstalled) return
|
|
23
|
+
const onPrepareForSave = platform('onPrepareForSave')
|
|
24
|
+
if (typeof onPrepareForSave !== 'function') return
|
|
25
|
+
onPrepareForSave((clonedDocEl) => {
|
|
26
|
+
restorePristineMarkup(clonedDocEl)
|
|
27
|
+
stripSessionChrome(clonedDocEl)
|
|
28
|
+
})
|
|
29
|
+
prepareHookInstalled = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// hypercms's own chrome lives on <body>, outside the [save-remove] shell, so it
|
|
33
|
+
// has to be taken off the clone by hand. Both hooks call this: the save hook
|
|
34
|
+
// because a client may offer onPrepareForSave and no onSnapshot, and the
|
|
35
|
+
// snapshot hook because that is the only one live sync reaches, and without it
|
|
36
|
+
// the session's own classes are broadcast to every other browser. Removing an
|
|
37
|
+
// absent class is a no-op, so running twice on a save costs nothing.
|
|
38
|
+
function stripSessionChrome(clonedDocEl) {
|
|
39
|
+
const b = clonedDocEl && clonedDocEl.querySelector && clonedDocEl.querySelector('body')
|
|
40
|
+
if (b) b.classList.remove('hcms-open', 'hcms-overlay', 'hcms-side-left', SESSION_OPEN_CLASS)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// A save can land while somebody is mid-session with an editor open on a
|
|
44
|
+
// heading. richclay rewrites a block's markup simply by being attached to it, so
|
|
45
|
+
// without this the file records that rewriting as though the author had typed
|
|
46
|
+
// it. The live editors are deliberately untouched: this is the clone, and the
|
|
47
|
+
// person editing keeps their caret and their selection.
|
|
48
|
+
function restorePristineMarkup(clonedDocEl) {
|
|
49
|
+
if (!clonedDocEl || typeof clonedDocEl.querySelectorAll !== 'function') return
|
|
50
|
+
for (const el of clonedDocEl.querySelectorAll(`[${BOUND_ID_ATTR}]`)) {
|
|
51
|
+
const html = pristineMarkupFor(el)
|
|
52
|
+
if (html !== null) el.innerHTML = html
|
|
53
|
+
el.removeAttribute(BOUND_ID_ATTR)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Clean up after the richclay instances hypercms itself creates on page
|
|
58
|
+
// elements. Installed once at first open, alongside the save-prepare hook above,
|
|
59
|
+
// and for the same reason: the host client may not be loaded yet at module
|
|
60
|
+
// evaluation. onSnapshot rather than onPrepareForSave, because this has to run
|
|
61
|
+
// for live sync too — a hook on the save-only path fires after the editor's
|
|
62
|
+
// leftovers have already been broadcast to every other browser.
|
|
63
|
+
let snapshotHookInstalled = false
|
|
64
|
+
export function installSnapshotHook() {
|
|
65
|
+
if (snapshotHookInstalled) return
|
|
66
|
+
const onSnapshot = platform('onSnapshot')
|
|
67
|
+
if (typeof onSnapshot !== 'function') return
|
|
68
|
+
onSnapshot((clonedDocEl) => {
|
|
69
|
+
restorePristineMarkup(clonedDocEl)
|
|
70
|
+
cleanRichClayFromSnapshot(clonedDocEl, typeof window !== 'undefined' ? window : null)
|
|
71
|
+
stripSessionChrome(clonedDocEl)
|
|
72
|
+
})
|
|
73
|
+
snapshotHookInstalled = true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// What open() calls: install both, and arm the retry when either could not be
|
|
77
|
+
// installed yet.
|
|
78
|
+
export function installHooks() {
|
|
79
|
+
installSavePrepareHook()
|
|
80
|
+
installSnapshotHook()
|
|
81
|
+
if (!prepareHookInstalled || !snapshotHookInstalled) armHookRetry()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Both hooks read a capability that can arrive after hypercms does, and both
|
|
85
|
+
// return without recording success when it is missing. Retry on the clients'
|
|
86
|
+
// readiness pair and disarm once both are in, so a page that lost the import
|
|
87
|
+
// race still gets its clone cleanup.
|
|
88
|
+
let offPlatformReady = null
|
|
89
|
+
function armHookRetry() {
|
|
90
|
+
if (offPlatformReady || typeof document === 'undefined') return
|
|
91
|
+
const retry = () => {
|
|
92
|
+
installSavePrepareHook()
|
|
93
|
+
installSnapshotHook()
|
|
94
|
+
if (prepareHookInstalled && snapshotHookInstalled) {
|
|
95
|
+
offPlatformReady?.()
|
|
96
|
+
offPlatformReady = null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
offPlatformReady = onPlatformEvent(document, PLATFORM_READY, retry)
|
|
100
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as core from './hypercms.js'
|
|
2
|
+
import themeText from './theme.generated.css'
|
|
3
|
+
// Install mirk's delegated component runtime (tags chips, number stepper, etc.)
|
|
4
|
+
// as a side effect. Idempotent + document-delegated, so safe to include once.
|
|
5
|
+
import './vendor/mirk.vendor.js'
|
|
6
|
+
|
|
7
|
+
core.installStyles(themeText)
|
|
8
|
+
|
|
9
|
+
export const cms = core.cms
|
|
10
|
+
export default { cms: core.cms }
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { engine } from 'hyper-html-api'
|
|
2
|
+
import * as pathUtil from './path.js'
|
|
3
|
+
import { scaffold } from './scaffold.js'
|
|
4
|
+
import { morphForm } from './morph.js'
|
|
5
|
+
import {
|
|
6
|
+
commit,
|
|
7
|
+
commitWithUndo,
|
|
8
|
+
onAdd as evOnAdd,
|
|
9
|
+
onRemove as evOnRemove,
|
|
10
|
+
extractFormData,
|
|
11
|
+
restampAllSiblings,
|
|
12
|
+
} from './events.js'
|
|
13
|
+
import { setShellStyles, markStylesBundled } from './shell.js'
|
|
14
|
+
import { createSidebarView } from './views/sidebar.js'
|
|
15
|
+
import { createInlineView } from './views/inline.js'
|
|
16
|
+
import {
|
|
17
|
+
state,
|
|
18
|
+
createSession,
|
|
19
|
+
startSession,
|
|
20
|
+
teardownSession,
|
|
21
|
+
nextSearchAfterClose,
|
|
22
|
+
shouldAutoOpenFromSearch,
|
|
23
|
+
} from './session.js'
|
|
24
|
+
import { findLeafField, writeFieldValue } from './events.js'
|
|
25
|
+
import { maybeInjectToggle } from './toggle.js'
|
|
26
|
+
import { platform, onPlatformEvent, MUTATION_READY } from './platform.js'
|
|
27
|
+
import { installHooks } from './hooks.js'
|
|
28
|
+
|
|
29
|
+
export { nextSearchAfterClose, shouldAutoOpenFromSearch }
|
|
30
|
+
// The leaf-write pair api.setValue is built on. Exported because the inline
|
|
31
|
+
// view's text commit writes the same form leaf from the page element, and a
|
|
32
|
+
// second copy of this logic is how the two paths drift apart.
|
|
33
|
+
export { findLeafField, writeFieldValue }
|
|
34
|
+
|
|
35
|
+
export function installStyles(text) {
|
|
36
|
+
setShellStyles(text)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function markBundledStyles(doc) {
|
|
40
|
+
markStylesBundled(doc)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The views a session can be rendered through. A name that is not in here is a
|
|
44
|
+
// caller error, never a fallback: silently opening a sidebar when someone asked
|
|
45
|
+
// for the inline editor would read as the inline editor being broken.
|
|
46
|
+
const VIEWS = {
|
|
47
|
+
sidebar: createSidebarView,
|
|
48
|
+
inline: createInlineView,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function open(opts = {}) {
|
|
52
|
+
// Everything that can be rejected is rejected before anything is destroyed. A
|
|
53
|
+
// bad view name or a missing page must not leave a caller with the session
|
|
54
|
+
// they had already torn down.
|
|
55
|
+
// A bare open() means "make sure the editor is up", not "put it in the
|
|
56
|
+
// sidebar". Defaulting to sidebar turned every such call into a view switch.
|
|
57
|
+
const viewName = opts.view || (state.isOpen ? state.ctx.view.name : 'sidebar')
|
|
58
|
+
const makeView = VIEWS[viewName]
|
|
59
|
+
if (!makeView) {
|
|
60
|
+
throw new Error(`hypercms: unknown view "${viewName}" (expected ${Object.keys(VIEWS).join(' or ')})`)
|
|
61
|
+
}
|
|
62
|
+
// A switch changes the presentation of one session, not the session. Someone
|
|
63
|
+
// calling open({ view: 'sidebar' }) means "show me this in the sidebar"; they
|
|
64
|
+
// are not restating the pageRoot, the rules and the callbacks they opened
|
|
65
|
+
// with. Resolving those from this call alone silently retargets the CMS at
|
|
66
|
+
// document.body with the default rules tag and stops calling onChange.
|
|
67
|
+
// state.opts holds the active session's options and teardown clears it, so it
|
|
68
|
+
// has to be read here, before anything is torn down.
|
|
69
|
+
const effective = state.isOpen ? { ...state.opts, ...opts, view: viewName } : opts
|
|
70
|
+
|
|
71
|
+
const pageRoot = effective.pageRoot || (typeof document !== 'undefined' ? document.body : null)
|
|
72
|
+
if (!pageRoot) throw new Error('hypercms: no pageRoot available')
|
|
73
|
+
const doc = pageRoot.ownerDocument || (typeof document !== 'undefined' ? document : null)
|
|
74
|
+
if (!doc) throw new Error('hypercms: no document available')
|
|
75
|
+
|
|
76
|
+
// Reopening the view that is already up is a silent no-op, not a warning: a
|
|
77
|
+
// host wiring a button to open({ view }) should be able to call it without
|
|
78
|
+
// first asking what is open. Switching views tears the old one down WITHOUT
|
|
79
|
+
// the close-only side effects — no hcms:close URL rewrite, no focus restore —
|
|
80
|
+
// because a switch is one continuous session from the person's side, not a
|
|
81
|
+
// close followed by an open.
|
|
82
|
+
let carriedFocus = null
|
|
83
|
+
let previous = null
|
|
84
|
+
if (state.isOpen) {
|
|
85
|
+
if (state.ctx.view.name === viewName) return
|
|
86
|
+
carriedFocus = state.ctx.previouslyFocused
|
|
87
|
+
previous = state.ctx.view.name
|
|
88
|
+
teardownSession(state.ctx, { restoreFocus: false, updateUrl: false, reason: 'switch' })
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
installHooks()
|
|
92
|
+
|
|
93
|
+
const view = makeView({ doc, pageRoot, opts: effective })
|
|
94
|
+
const ctx = createSession({
|
|
95
|
+
view,
|
|
96
|
+
doc,
|
|
97
|
+
pageRoot,
|
|
98
|
+
opts: effective,
|
|
99
|
+
onCloseRequested: () => close(),
|
|
100
|
+
onViewRequested: (name) => open({ view: name }),
|
|
101
|
+
})
|
|
102
|
+
// Where focus was before the FIRST open, carried across the switch. Closing
|
|
103
|
+
// the second view should return the person where they started, not to
|
|
104
|
+
// whatever held focus after the first view was pulled out from under them.
|
|
105
|
+
if (carriedFocus) ctx.previouslyFocused = carriedFocus
|
|
106
|
+
|
|
107
|
+
// Everything past here touches the live DOM, the module-level state, and
|
|
108
|
+
// external subscriptions (observer, undo, livesync). A throw in here — most
|
|
109
|
+
// notably installObserver when window.hyperclay.Mutation isn't loaded — would
|
|
110
|
+
// otherwise strand the mounted view in the DOM with no way to close it (close()
|
|
111
|
+
// early-returns while state.isOpen is false). Tear down whatever was wired,
|
|
112
|
+
// without the close-only side effects (no hcms:close, no URL rewrite, no focus
|
|
113
|
+
// restore), then rethrow so host misuse still fails loudly.
|
|
114
|
+
try {
|
|
115
|
+
view.mount(ctx.initialData)
|
|
116
|
+
startSession(ctx)
|
|
117
|
+
view.focusOnOpen()
|
|
118
|
+
|
|
119
|
+
state.isOpen = true
|
|
120
|
+
state.ctx = ctx
|
|
121
|
+
state.opts = effective
|
|
122
|
+
|
|
123
|
+
ctx.dispatch('hcms:open', { pageRoot, previous })
|
|
124
|
+
} catch (err) {
|
|
125
|
+
// A failed switch already destroyed the view that was up, so nothing is
|
|
126
|
+
// mounted and nothing can be reopened; the least bad outcome is handing
|
|
127
|
+
// focus back where it started. On a first open focus never moved, so
|
|
128
|
+
// restoring it there would be a no-op either way.
|
|
129
|
+
teardownSession(ctx, { dispatch: false, restoreFocus: !!carriedFocus, updateUrl: false })
|
|
130
|
+
throw err
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function close() {
|
|
135
|
+
if (!state.isOpen) return
|
|
136
|
+
teardownSession(state.ctx)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function refresh() {
|
|
140
|
+
if (!state.isOpen) return
|
|
141
|
+
state.ctx.view.refresh('api')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function isOpen() {
|
|
145
|
+
return state.isOpen
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Which view owns the active session. Nothing may infer this from
|
|
149
|
+
// body.hcms-open, which describes only the sidebar's page shift and says nothing
|
|
150
|
+
// about an inline session.
|
|
151
|
+
export function currentView() {
|
|
152
|
+
return state.isOpen && state.ctx ? state.ctx.view.name : null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const api = {
|
|
156
|
+
getData() {
|
|
157
|
+
if (!state.isOpen) return null
|
|
158
|
+
return extractFormData(state.ctx)
|
|
159
|
+
},
|
|
160
|
+
setValue(path, value) {
|
|
161
|
+
if (!state.isOpen) throw new Error('hypercms: cms is not open')
|
|
162
|
+
const ctx = state.ctx
|
|
163
|
+
const pathArr = pathUtil.fromString(path)
|
|
164
|
+
const rule = pathUtil.getRuleAtPath(ctx.pageRules, pathArr)
|
|
165
|
+
if (rule === undefined) throw new Error(`hypercms: no rule at path "${path}"`)
|
|
166
|
+
if (typeof rule !== 'string' || rule.endsWith('[]')) {
|
|
167
|
+
throw new Error(`hypercms: setValue requires a leaf scalar path; "${path}" is not a leaf`)
|
|
168
|
+
}
|
|
169
|
+
const field = findLeafField(ctx.formRoot, path)
|
|
170
|
+
if (!field) throw new Error(`hypercms: no field element at path "${path}"`)
|
|
171
|
+
writeFieldValue(field, value, ctx.formRoot, path)
|
|
172
|
+
commit(extractFormData(ctx), { path, structural: false }, ctx)
|
|
173
|
+
},
|
|
174
|
+
addItem(arrayPath) {
|
|
175
|
+
if (!state.isOpen) throw new Error('hypercms: cms is not open')
|
|
176
|
+
evOnAdd(arrayPath, state.ctx)
|
|
177
|
+
},
|
|
178
|
+
removeItem(itemPath) {
|
|
179
|
+
if (!state.isOpen) throw new Error('hypercms: cms is not open')
|
|
180
|
+
const ctx = state.ctx
|
|
181
|
+
const pathArr = pathUtil.fromString(itemPath)
|
|
182
|
+
// Validate path: must be an array item — last segment is a non-negative
|
|
183
|
+
// integer, parent rule must be an array (object-array or scalar-array).
|
|
184
|
+
const lastSeg = pathArr[pathArr.length - 1]
|
|
185
|
+
if (typeof lastSeg !== 'number') {
|
|
186
|
+
throw new Error(`hypercms: removeItem requires an item path; "${itemPath}" is not an array index`)
|
|
187
|
+
}
|
|
188
|
+
const parentRule = pathUtil.getRuleAtPath(ctx.pageRules, pathArr.slice(0, -1))
|
|
189
|
+
const parentIsArray = Array.isArray(parentRule) || (typeof parentRule === 'string' && parentRule.endsWith('[]'))
|
|
190
|
+
if (!parentIsArray) {
|
|
191
|
+
throw new Error(`hypercms: removeItem requires an item path; parent of "${itemPath}" is not an array`)
|
|
192
|
+
}
|
|
193
|
+
const itemEl = ctx.formRoot.querySelector(`[data-hcms-path="${cssEscape(itemPath)}"]`)
|
|
194
|
+
if (!itemEl) throw new Error(`hypercms: no element at path "${itemPath}"`)
|
|
195
|
+
evOnRemove(itemEl, ctx)
|
|
196
|
+
},
|
|
197
|
+
refresh,
|
|
198
|
+
_commit() {
|
|
199
|
+
if (!state.isOpen) return
|
|
200
|
+
const ctx = state.ctx
|
|
201
|
+
restampAllSiblings(ctx.formRoot)
|
|
202
|
+
return commitWithUndo('Update', () =>
|
|
203
|
+
commit(extractFormData(ctx), { path: '', structural: true }, ctx)
|
|
204
|
+
)
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ?cms=true auto-open. When the page URL carries cms=true at load, open the CMS
|
|
209
|
+
// for everyone (no gating — this is a feature showcase; a non-admin's edits stay
|
|
210
|
+
// DOM-local and the host save path shows its view-mode notice). open() is
|
|
211
|
+
// idempotent-safe (it returns quietly if the same view is already open), so a
|
|
212
|
+
// host that also calls open() never double-mounts. No special opts — host
|
|
213
|
+
// defaults apply.
|
|
214
|
+
//
|
|
215
|
+
// Patience matters here: this runs at module load from the dist IIFE, which a
|
|
216
|
+
// host typically loads BEFORE installing window.hyperclay.Mutation (the Mutation
|
|
217
|
+
// install often sits behind an `await import(...)` that resumes after this script,
|
|
218
|
+
// even after DOMContentLoaded). open() needs <body> AND Mutation, so we poll for
|
|
219
|
+
// both rather than firing eagerly. Firing without Mutation would throw out of
|
|
220
|
+
// installObserver, escape module evaluation, and abort esbuild's
|
|
221
|
+
// window.hypercms assignment — taking the host's own wiring down with it.
|
|
222
|
+
const SAFETY_POLL_MS = 250
|
|
223
|
+
const AUTO_OPEN_DEADLINE_MS = 10000
|
|
224
|
+
export function maybeAutoOpen() {
|
|
225
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return
|
|
226
|
+
if (!shouldAutoOpenFromSearch(window.location ? window.location.search : '')) return
|
|
227
|
+
if (state.isOpen) return
|
|
228
|
+
whenReady(() => {
|
|
229
|
+
if (state.isOpen) return
|
|
230
|
+
try {
|
|
231
|
+
open()
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.warn('hypercms: auto-open failed', err)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Auto-open is ready only once <body> is parsed AND window.hyperclay.Mutation is
|
|
239
|
+
// installed (open() needs both). NEVER throw out of module scope.
|
|
240
|
+
function autoOpenReady() {
|
|
241
|
+
return !!document.body && !!platform('Mutation')
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Three layers, in order of preference:
|
|
245
|
+
// 1. Sync fast-path: if <body> and Mutation are already here, fire now.
|
|
246
|
+
// 2. Event: both clients dispatch a mutation-ready signal the instant they
|
|
247
|
+
// install the mutation hub, under two spellings ('clay:mutation-ready' and
|
|
248
|
+
// 'hyperclay:mutation-ready'), so hypercms listens for both and dedups. On
|
|
249
|
+
// loader pages this is moot — the loader's first wave evaluates mutation
|
|
250
|
+
// before hypercms, so the fast-path wins — but the event catches any late
|
|
251
|
+
// install (the deferred `await import()` ordering). The body gate is
|
|
252
|
+
// re-checked in the handler because the event can land before <body> is
|
|
253
|
+
// parsed.
|
|
254
|
+
// 3. Backstop: a slow self-cancelling 250ms poll for exotic hosts that hand-roll
|
|
255
|
+
// window.hyperclay.Mutation by direct assignment WITHOUT dispatching the
|
|
256
|
+
// event. It cancels the moment the event fires, open succeeds, or the
|
|
257
|
+
// deadline is hit (warn, never throw).
|
|
258
|
+
function whenReady(fn) {
|
|
259
|
+
if (autoOpenReady()) {
|
|
260
|
+
// Defer one microtask. On the bundled entry (hypercms-bundle.js), installStyles()
|
|
261
|
+
// runs AFTER this module evaluates, so firing open() synchronously here would
|
|
262
|
+
// mount the shell with cssText still empty (an unstyled sidebar). A microtask
|
|
263
|
+
// runs only after the synchronous bundle evaluation completes, by which point
|
|
264
|
+
// styles are installed. The event/backstop paths below already fire post-eval,
|
|
265
|
+
// so only this fast path needed deferring.
|
|
266
|
+
queueMicrotask(fn)
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
const deadline = Date.now() + AUTO_OPEN_DEADLINE_MS
|
|
270
|
+
let done = false
|
|
271
|
+
let timer = null
|
|
272
|
+
let offMutationReady = null
|
|
273
|
+
const finish = () => {
|
|
274
|
+
if (done) return
|
|
275
|
+
done = true
|
|
276
|
+
if (timer !== null) clearInterval(timer)
|
|
277
|
+
if (offMutationReady) offMutationReady()
|
|
278
|
+
}
|
|
279
|
+
function onMutationReady() {
|
|
280
|
+
if (state.isOpen) {
|
|
281
|
+
finish()
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
if (autoOpenReady()) {
|
|
285
|
+
finish()
|
|
286
|
+
fn()
|
|
287
|
+
}
|
|
288
|
+
// Body may still be pending. Unlike the old one-shot listener this stays
|
|
289
|
+
// subscribed, so a later dispatch is still caught and the backstop is no
|
|
290
|
+
// longer the only remaining path.
|
|
291
|
+
}
|
|
292
|
+
offMutationReady = onPlatformEvent(document, MUTATION_READY, onMutationReady)
|
|
293
|
+
timer = setInterval(() => {
|
|
294
|
+
if (state.isOpen) {
|
|
295
|
+
finish()
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
if (autoOpenReady()) {
|
|
299
|
+
finish()
|
|
300
|
+
fn()
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
if (Date.now() >= deadline) {
|
|
304
|
+
finish()
|
|
305
|
+
console.warn(
|
|
306
|
+
'hypercms: ?cms=true auto-open gave up — no mutation hub appeared. ' +
|
|
307
|
+
'Load clayjs or hyperclayjs (or just the mutation utility) so the CMS can initialize.'
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
}, SAFETY_POLL_MS)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Auto-open at module load (browser only — node tests import this without a real
|
|
314
|
+
// window, and the guards above no-op there).
|
|
315
|
+
maybeAutoOpen()
|
|
316
|
+
|
|
317
|
+
// Floating edit-mode toggle at module load (same browser-only no-op in node).
|
|
318
|
+
maybeInjectToggle({
|
|
319
|
+
open,
|
|
320
|
+
close,
|
|
321
|
+
isOpen,
|
|
322
|
+
// The same map open() resolves a view name against, so the split button
|
|
323
|
+
// offers exactly the views this build can actually render.
|
|
324
|
+
views: Object.keys(VIEWS),
|
|
325
|
+
hasRules: (doc) => !!engine.findRules(doc, 'cms'),
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
const cms = {
|
|
329
|
+
open,
|
|
330
|
+
close,
|
|
331
|
+
refresh,
|
|
332
|
+
api,
|
|
333
|
+
get isOpen() { return state.isOpen },
|
|
334
|
+
currentView,
|
|
335
|
+
// Power-user exports (mostly for testing + advanced integration)
|
|
336
|
+
path: pathUtil,
|
|
337
|
+
scaffold,
|
|
338
|
+
morphForm,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function cssEscape(value) {
|
|
342
|
+
if (typeof CSS !== 'undefined' && CSS.escape) return CSS.escape(value)
|
|
343
|
+
return String(value).replace(/[^a-zA-Z0-9_\-.*]/g, (c) => '\\' + c)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export { cms }
|
|
347
|
+
export default { cms }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import HyperMorph from 'hyper-morph'
|
|
2
|
+
|
|
3
|
+
export function morphForm(formRoot, newFragment, { ignoreActiveValue = true } = {}) {
|
|
4
|
+
HyperMorph.morph(formRoot, newFragment, {
|
|
5
|
+
morphStyle: 'innerHTML',
|
|
6
|
+
ignoreActiveValue,
|
|
7
|
+
restoreFocus: true,
|
|
8
|
+
formStateSync: 'property',
|
|
9
|
+
})
|
|
10
|
+
}
|