aeon-ui-engine 1.0.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/.changeset/README.md +23 -0
- package/.changeset/combobox-and-changesets.md +9 -0
- package/.changeset/config.json +30 -0
- package/.changeset/framework-adapters.md +12 -0
- package/.changeset/scene-3d.md +6 -0
- package/.changeset/scene-plane.md +8 -0
- package/.changeset/site-og.md +5 -0
- package/.cursor/rules/aeon-ui.mdc +22 -0
- package/ACCESSIBILITY.md +32 -0
- package/AGENTS.md +226 -0
- package/COMPONENTS.md +36 -0
- package/FRAMEWORKS.md +57 -0
- package/INTEGRATION.md +89 -0
- package/LICENSE +21 -0
- package/PHILOSOPHY.md +71 -0
- package/PUBLISHING.md +85 -0
- package/README.md +154 -0
- package/SCENE.md +113 -0
- package/STATES.md +243 -0
- package/apps/demo/index.html +31 -0
- package/apps/demo/og.config.mjs +15 -0
- package/apps/demo/package.json +41 -0
- package/apps/demo/public/favicon.svg +16 -0
- package/apps/demo/public/og.png +0 -0
- package/apps/demo/public/og.svg +35 -0
- package/apps/demo/scripts/generate-og-png.mjs +60 -0
- package/apps/demo/src/App.tsx +31 -0
- package/apps/demo/src/aeonDiagrams.ts +100 -0
- package/apps/demo/src/app.css +16 -0
- package/apps/demo/src/components/ActionCluster.tsx +20 -0
- package/apps/demo/src/components/AsyncMachineDemo.tsx +97 -0
- package/apps/demo/src/components/ButtonLifecycleDemo.tsx +63 -0
- package/apps/demo/src/components/CodeExample.tsx +7 -0
- package/apps/demo/src/components/ComboboxDemo.tsx +30 -0
- package/apps/demo/src/components/DiagramsSection.tsx +38 -0
- package/apps/demo/src/components/FieldStateDemo.tsx +51 -0
- package/apps/demo/src/components/GridMesh.tsx +8 -0
- package/apps/demo/src/components/HeroInstall.tsx +10 -0
- package/apps/demo/src/components/LandingFaq.tsx +29 -0
- package/apps/demo/src/components/LandingFooter.tsx +22 -0
- package/apps/demo/src/components/LandingHeader.tsx +60 -0
- package/apps/demo/src/components/LandingHero.tsx +55 -0
- package/apps/demo/src/components/LayersSection.tsx +51 -0
- package/apps/demo/src/components/MermaidDiagram.tsx +84 -0
- package/apps/demo/src/components/PhilosophySection.tsx +33 -0
- package/apps/demo/src/components/PlaygroundBlock.tsx +23 -0
- package/apps/demo/src/components/PlaygroundSection.tsx +204 -0
- package/apps/demo/src/components/SceneSection.tsx +157 -0
- package/apps/demo/src/components/ScrollRegionDemo.tsx +74 -0
- package/apps/demo/src/components/ThemeSwitcher.tsx +76 -0
- package/apps/demo/src/demo-layout.css +264 -0
- package/apps/demo/src/landing.css +1094 -0
- package/apps/demo/src/main.tsx +14 -0
- package/apps/demo/src/nav.ts +9 -0
- package/apps/demo/src/site.ts +347 -0
- package/apps/demo/tsconfig.json +17 -0
- package/apps/demo/vite-plugins/og-meta.ts +30 -0
- package/apps/demo/vite.config.ts +69 -0
- package/docs/API_PATTERNS.md +234 -0
- package/docs/ARCHITECTURE.md +125 -0
- package/docs/COMPONENT_CHECKLIST.md +103 -0
- package/docs/FILE_MAP.md +122 -0
- package/docs/GLOSSARY.md +125 -0
- package/docs/INDEX.md +53 -0
- package/docs/LAYOUT_COORDINATES.md +178 -0
- package/docs/MACHINES.md +166 -0
- package/docs/MIGRATION_FROM_ARK.md +295 -0
- package/docs/VITE_CONSUMER.md +114 -0
- package/package.json +25 -0
- package/packages/core/README.md +69 -0
- package/packages/core/package.json +37 -0
- package/packages/core/src/anatomy.ts +144 -0
- package/packages/core/src/attrs.ts +47 -0
- package/packages/core/src/index.ts +8 -0
- package/packages/core/src/layout.ts +176 -0
- package/packages/core/src/machine.test.ts +18 -0
- package/packages/core/src/machine.ts +15 -0
- package/packages/core/src/philosophy.ts +10 -0
- package/packages/core/src/scroll.ts +72 -0
- package/packages/core/src/theme.test.ts +14 -0
- package/packages/core/src/theme.ts +53 -0
- package/packages/core/src/types.ts +25 -0
- package/packages/core/theme-transition.css +58 -0
- package/packages/core/tsconfig.json +8 -0
- package/packages/core/vitest.config.ts +8 -0
- package/packages/geometry/README.md +7 -0
- package/packages/geometry/package.json +35 -0
- package/packages/geometry/src/index.ts +3 -0
- package/packages/geometry/src/patterns.ts +55 -0
- package/packages/geometry/src/scene-attrs.ts +11 -0
- package/packages/geometry/src/svg.ts +60 -0
- package/packages/geometry/tsconfig.json +8 -0
- package/packages/panda/README.md +66 -0
- package/packages/panda/package.json +40 -0
- package/packages/panda/panda.config.ts +229 -0
- package/packages/panda/scripts/emit-theme-runtime.ts +37 -0
- package/packages/panda/src/recipes/accordion.ts +95 -0
- package/packages/panda/src/recipes/async.ts +76 -0
- package/packages/panda/src/recipes/badge.ts +44 -0
- package/packages/panda/src/recipes/button-group.ts +33 -0
- package/packages/panda/src/recipes/button.ts +91 -0
- package/packages/panda/src/recipes/checkbox.ts +41 -0
- package/packages/panda/src/recipes/combobox.ts +121 -0
- package/packages/panda/src/recipes/dialog.ts +96 -0
- package/packages/panda/src/recipes/field-shared.ts +29 -0
- package/packages/panda/src/recipes/field.ts +72 -0
- package/packages/panda/src/recipes/floating.ts +170 -0
- package/packages/panda/src/recipes/index.ts +14 -0
- package/packages/panda/src/recipes/scroll-region.ts +55 -0
- package/packages/panda/src/recipes/select.ts +111 -0
- package/packages/panda/src/recipes/switch.ts +51 -0
- package/packages/panda/src/recipes/tabs.ts +50 -0
- package/packages/panda/src/theme/catalog-types.ts +24 -0
- package/packages/panda/src/theme/catalog.ts +73 -0
- package/packages/panda/src/theme/chrome.css +75 -0
- package/packages/panda/src/theme/global-theme-css.ts +42 -0
- package/packages/panda/src/theme/index.ts +5 -0
- package/packages/panda/src/theme/palette.ts +65 -0
- package/packages/panda/src/theme/scrollbars.css +96 -0
- package/packages/panda/src/theme/semantic-tokens.ts +17 -0
- package/packages/panda/src/theme/themes.config.ts +516 -0
- package/packages/panda/src/theme/tokens.ts +51 -0
- package/packages/panda/styled-system/css/conditions.mjs +36 -0
- package/packages/panda/styled-system/css/css.d.ts +22 -0
- package/packages/panda/styled-system/css/css.mjs +45 -0
- package/packages/panda/styled-system/css/cva.d.ts +6 -0
- package/packages/panda/styled-system/css/cva.mjs +87 -0
- package/packages/panda/styled-system/css/cx.d.ts +5 -0
- package/packages/panda/styled-system/css/cx.mjs +15 -0
- package/packages/panda/styled-system/css/index.d.ts +5 -0
- package/packages/panda/styled-system/css/index.mjs +4 -0
- package/packages/panda/styled-system/css/sva.d.ts +4 -0
- package/packages/panda/styled-system/css/sva.mjs +41 -0
- package/packages/panda/styled-system/helpers.mjs +339 -0
- package/packages/panda/styled-system/jsx/aspect-ratio.d.ts +10 -0
- package/packages/panda/styled-system/jsx/aspect-ratio.mjs +14 -0
- package/packages/panda/styled-system/jsx/bleed.d.ts +10 -0
- package/packages/panda/styled-system/jsx/bleed.mjs +14 -0
- package/packages/panda/styled-system/jsx/box.d.ts +10 -0
- package/packages/panda/styled-system/jsx/box.mjs +14 -0
- package/packages/panda/styled-system/jsx/center.d.ts +10 -0
- package/packages/panda/styled-system/jsx/center.mjs +14 -0
- package/packages/panda/styled-system/jsx/circle.d.ts +10 -0
- package/packages/panda/styled-system/jsx/circle.mjs +14 -0
- package/packages/panda/styled-system/jsx/container.d.ts +10 -0
- package/packages/panda/styled-system/jsx/container.mjs +14 -0
- package/packages/panda/styled-system/jsx/cq.d.ts +10 -0
- package/packages/panda/styled-system/jsx/cq.mjs +14 -0
- package/packages/panda/styled-system/jsx/divider.d.ts +10 -0
- package/packages/panda/styled-system/jsx/divider.mjs +14 -0
- package/packages/panda/styled-system/jsx/factory-helper.mjs +22 -0
- package/packages/panda/styled-system/jsx/factory.d.ts +3 -0
- package/packages/panda/styled-system/jsx/factory.mjs +80 -0
- package/packages/panda/styled-system/jsx/flex.d.ts +10 -0
- package/packages/panda/styled-system/jsx/flex.mjs +14 -0
- package/packages/panda/styled-system/jsx/float.d.ts +10 -0
- package/packages/panda/styled-system/jsx/float.mjs +14 -0
- package/packages/panda/styled-system/jsx/grid-item.d.ts +10 -0
- package/packages/panda/styled-system/jsx/grid-item.mjs +14 -0
- package/packages/panda/styled-system/jsx/grid.d.ts +10 -0
- package/packages/panda/styled-system/jsx/grid.mjs +14 -0
- package/packages/panda/styled-system/jsx/hstack.d.ts +10 -0
- package/packages/panda/styled-system/jsx/hstack.mjs +14 -0
- package/packages/panda/styled-system/jsx/index.d.ts +24 -0
- package/packages/panda/styled-system/jsx/index.mjs +22 -0
- package/packages/panda/styled-system/jsx/is-valid-prop.d.ts +11 -0
- package/packages/panda/styled-system/jsx/is-valid-prop.mjs +17 -0
- package/packages/panda/styled-system/jsx/link-overlay.d.ts +10 -0
- package/packages/panda/styled-system/jsx/link-overlay.mjs +14 -0
- package/packages/panda/styled-system/jsx/spacer.d.ts +10 -0
- package/packages/panda/styled-system/jsx/spacer.mjs +14 -0
- package/packages/panda/styled-system/jsx/square.d.ts +10 -0
- package/packages/panda/styled-system/jsx/square.mjs +14 -0
- package/packages/panda/styled-system/jsx/stack.d.ts +10 -0
- package/packages/panda/styled-system/jsx/stack.mjs +14 -0
- package/packages/panda/styled-system/jsx/visually-hidden.d.ts +10 -0
- package/packages/panda/styled-system/jsx/visually-hidden.mjs +14 -0
- package/packages/panda/styled-system/jsx/vstack.d.ts +10 -0
- package/packages/panda/styled-system/jsx/vstack.mjs +14 -0
- package/packages/panda/styled-system/jsx/wrap.d.ts +10 -0
- package/packages/panda/styled-system/jsx/wrap.mjs +14 -0
- package/packages/panda/styled-system/patterns/aspect-ratio.d.ts +21 -0
- package/packages/panda/styled-system/patterns/aspect-ratio.mjs +38 -0
- package/packages/panda/styled-system/patterns/bleed.d.ts +22 -0
- package/packages/panda/styled-system/patterns/bleed.mjs +24 -0
- package/packages/panda/styled-system/patterns/box.d.ts +21 -0
- package/packages/panda/styled-system/patterns/box.mjs +15 -0
- package/packages/panda/styled-system/patterns/center.d.ts +21 -0
- package/packages/panda/styled-system/patterns/center.mjs +21 -0
- package/packages/panda/styled-system/patterns/circle.d.ts +21 -0
- package/packages/panda/styled-system/patterns/circle.mjs +25 -0
- package/packages/panda/styled-system/patterns/container.d.ts +21 -0
- package/packages/panda/styled-system/patterns/container.mjs +21 -0
- package/packages/panda/styled-system/patterns/cq.d.ts +22 -0
- package/packages/panda/styled-system/patterns/cq.mjs +21 -0
- package/packages/panda/styled-system/patterns/divider.d.ts +23 -0
- package/packages/panda/styled-system/patterns/divider.mjs +25 -0
- package/packages/panda/styled-system/patterns/flex.d.ts +27 -0
- package/packages/panda/styled-system/patterns/flex.mjs +26 -0
- package/packages/panda/styled-system/patterns/float.d.ts +24 -0
- package/packages/panda/styled-system/patterns/float.mjs +52 -0
- package/packages/panda/styled-system/patterns/grid-item.d.ts +26 -0
- package/packages/panda/styled-system/patterns/grid-item.mjs +25 -0
- package/packages/panda/styled-system/patterns/grid.d.ts +25 -0
- package/packages/panda/styled-system/patterns/grid.mjs +27 -0
- package/packages/panda/styled-system/patterns/hstack.d.ts +22 -0
- package/packages/panda/styled-system/patterns/hstack.mjs +24 -0
- package/packages/panda/styled-system/patterns/index.d.ts +21 -0
- package/packages/panda/styled-system/patterns/index.mjs +20 -0
- package/packages/panda/styled-system/patterns/link-overlay.d.ts +21 -0
- package/packages/panda/styled-system/patterns/link-overlay.mjs +24 -0
- package/packages/panda/styled-system/patterns/spacer.d.ts +21 -0
- package/packages/panda/styled-system/patterns/spacer.mjs +21 -0
- package/packages/panda/styled-system/patterns/square.d.ts +21 -0
- package/packages/panda/styled-system/patterns/square.mjs +24 -0
- package/packages/panda/styled-system/patterns/stack.d.ts +24 -0
- package/packages/panda/styled-system/patterns/stack.mjs +24 -0
- package/packages/panda/styled-system/patterns/visually-hidden.d.ts +21 -0
- package/packages/panda/styled-system/patterns/visually-hidden.mjs +18 -0
- package/packages/panda/styled-system/patterns/vstack.d.ts +22 -0
- package/packages/panda/styled-system/patterns/vstack.mjs +24 -0
- package/packages/panda/styled-system/patterns/wrap.d.ts +25 -0
- package/packages/panda/styled-system/patterns/wrap.mjs +25 -0
- package/packages/panda/styled-system/recipes/aeon-accordion.d.ts +32 -0
- package/packages/panda/styled-system/recipes/aeon-accordion.mjs +48 -0
- package/packages/panda/styled-system/recipes/aeon-async.d.ts +32 -0
- package/packages/panda/styled-system/recipes/aeon-async.mjs +56 -0
- package/packages/panda/styled-system/recipes/aeon-badge.d.ts +31 -0
- package/packages/panda/styled-system/recipes/aeon-badge.mjs +32 -0
- package/packages/panda/styled-system/recipes/aeon-button-group.d.ts +39 -0
- package/packages/panda/styled-system/recipes/aeon-button-group.mjs +37 -0
- package/packages/panda/styled-system/recipes/aeon-button.d.ts +39 -0
- package/packages/panda/styled-system/recipes/aeon-button.mjs +39 -0
- package/packages/panda/styled-system/recipes/aeon-checkbox.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-checkbox.mjs +48 -0
- package/packages/panda/styled-system/recipes/aeon-combobox.d.ts +32 -0
- package/packages/panda/styled-system/recipes/aeon-combobox.mjs +52 -0
- package/packages/panda/styled-system/recipes/aeon-dialog.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-dialog.mjs +60 -0
- package/packages/panda/styled-system/recipes/aeon-field.d.ts +32 -0
- package/packages/panda/styled-system/recipes/aeon-field.mjs +52 -0
- package/packages/panda/styled-system/recipes/aeon-menu.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-menu.mjs +52 -0
- package/packages/panda/styled-system/recipes/aeon-popover.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-popover.mjs +52 -0
- package/packages/panda/styled-system/recipes/aeon-scroll.d.ts +43 -0
- package/packages/panda/styled-system/recipes/aeon-scroll.mjs +63 -0
- package/packages/panda/styled-system/recipes/aeon-select.d.ts +32 -0
- package/packages/panda/styled-system/recipes/aeon-select.mjs +56 -0
- package/packages/panda/styled-system/recipes/aeon-switch.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-switch.mjs +48 -0
- package/packages/panda/styled-system/recipes/aeon-tabs.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-tabs.mjs +48 -0
- package/packages/panda/styled-system/recipes/aeon-toast.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-toast.mjs +48 -0
- package/packages/panda/styled-system/recipes/aeon-tooltip.d.ts +28 -0
- package/packages/panda/styled-system/recipes/aeon-tooltip.mjs +48 -0
- package/packages/panda/styled-system/recipes/create-recipe.mjs +82 -0
- package/packages/panda/styled-system/recipes/index.d.ts +18 -0
- package/packages/panda/styled-system/recipes/index.mjs +17 -0
- package/packages/panda/styled-system/styles.css +2157 -0
- package/packages/panda/styled-system/tokens/index.d.ts +9 -0
- package/packages/panda/styled-system/tokens/index.mjs +2156 -0
- package/packages/panda/styled-system/tokens/tokens.d.ts +60 -0
- package/packages/panda/styled-system/types/composition.d.ts +164 -0
- package/packages/panda/styled-system/types/conditions.d.ts +322 -0
- package/packages/panda/styled-system/types/csstype.d.ts +21298 -0
- package/packages/panda/styled-system/types/global.d.ts +20 -0
- package/packages/panda/styled-system/types/index.d.ts +8 -0
- package/packages/panda/styled-system/types/jsx.d.ts +52 -0
- package/packages/panda/styled-system/types/parts.d.ts +8 -0
- package/packages/panda/styled-system/types/pattern.d.ts +78 -0
- package/packages/panda/styled-system/types/prop-type.d.ts +240 -0
- package/packages/panda/styled-system/types/recipe.d.ts +181 -0
- package/packages/panda/styled-system/types/selectors.d.ts +59 -0
- package/packages/panda/styled-system/types/static-css.d.ts +56 -0
- package/packages/panda/styled-system/types/style-props.d.ts +7491 -0
- package/packages/panda/styled-system/types/system-types.d.ts +193 -0
- package/packages/panda/theme-runtime.css +842 -0
- package/packages/panda/tsconfig.json +8 -0
- package/packages/primitives/README.md +54 -0
- package/packages/primitives/package.json +41 -0
- package/packages/primitives/src/__tests__/floating-surface.test.ts +29 -0
- package/packages/primitives/src/index.ts +9 -0
- package/packages/primitives/src/machines/accordion.ts +56 -0
- package/packages/primitives/src/machines/async.ts +108 -0
- package/packages/primitives/src/machines/button.ts +66 -0
- package/packages/primitives/src/machines/dialog.ts +61 -0
- package/packages/primitives/src/machines/field.ts +85 -0
- package/packages/primitives/src/machines/popover.ts +52 -0
- package/packages/primitives/src/machines/tabs.ts +46 -0
- package/packages/primitives/src/machines/toast.ts +48 -0
- package/packages/primitives/src/machines/toggle.ts +61 -0
- package/packages/primitives/tsconfig.json +8 -0
- package/packages/primitives/vitest.config.ts +8 -0
- package/packages/react/README.md +65 -0
- package/packages/react/package.json +52 -0
- package/packages/react/src/accordion/index.tsx +179 -0
- package/packages/react/src/async/actions.tsx +22 -0
- package/packages/react/src/async/context.tsx +54 -0
- package/packages/react/src/async/index.tsx +21 -0
- package/packages/react/src/async/legacy-region.tsx +38 -0
- package/packages/react/src/async/readout.tsx +34 -0
- package/packages/react/src/async/root.tsx +39 -0
- package/packages/react/src/async/track.tsx +59 -0
- package/packages/react/src/avatar/avatar.test.tsx +27 -0
- package/packages/react/src/avatar/index.tsx +122 -0
- package/packages/react/src/button/index.tsx +70 -0
- package/packages/react/src/checkbox/index.tsx +180 -0
- package/packages/react/src/combobox/index.tsx +430 -0
- package/packages/react/src/dialog/dialog.test.tsx +50 -0
- package/packages/react/src/dialog/index.tsx +275 -0
- package/packages/react/src/field/index.tsx +172 -0
- package/packages/react/src/hooks/use-aeon-machine.ts +10 -0
- package/packages/react/src/hooks/use-anchor-position.ts +33 -0
- package/packages/react/src/hooks/use-focus-trap.ts +51 -0
- package/packages/react/src/hooks/use-layout-mode.ts +22 -0
- package/packages/react/src/hooks/use-outside-click.ts +19 -0
- package/packages/react/src/hooks/use-portalled.ts +16 -0
- package/packages/react/src/hooks/use-scroll-lock.ts +13 -0
- package/packages/react/src/index.ts +18 -0
- package/packages/react/src/menu/index.tsx +195 -0
- package/packages/react/src/popover/index.tsx +191 -0
- package/packages/react/src/scroll/index.tsx +98 -0
- package/packages/react/src/scroll/use-scroll-state.ts +30 -0
- package/packages/react/src/select/index.tsx +372 -0
- package/packages/react/src/select/select.test.tsx +60 -0
- package/packages/react/src/switch/index.tsx +215 -0
- package/packages/react/src/tabs/index.tsx +140 -0
- package/packages/react/src/toast/context.tsx +28 -0
- package/packages/react/src/toast/index.tsx +20 -0
- package/packages/react/src/toast/primitives.tsx +129 -0
- package/packages/react/src/toast/provider.tsx +111 -0
- package/packages/react/src/toast/toast-item.tsx +32 -0
- package/packages/react/src/tooltip/index.tsx +191 -0
- package/packages/react/src/utils/merge-props.ts +24 -0
- package/packages/react/tsconfig.json +8 -0
- package/packages/react/vitest.config.ts +9 -0
- package/packages/react/vitest.setup.ts +5 -0
- package/packages/react-scene/README.md +11 -0
- package/packages/react-scene/package.json +47 -0
- package/packages/react-scene/scene-3d.css +37 -0
- package/packages/react-scene/scene-room.css +95 -0
- package/packages/react-scene/src/index.ts +20 -0
- package/packages/react-scene/src/procedural-grid.tsx +35 -0
- package/packages/react-scene/src/scene-layer.tsx +38 -0
- package/packages/react-scene/src/scene-plane.tsx +69 -0
- package/packages/react-scene/src/scene-room.tsx +81 -0
- package/packages/react-scene/src/scene-root.tsx +49 -0
- package/packages/react-scene/src/scene-slot.tsx +41 -0
- package/packages/react-scene/src/scene-stage.tsx +37 -0
- package/packages/react-scene/src/scene-surface.tsx +20 -0
- package/packages/react-scene/src/signal-bars.tsx +57 -0
- package/packages/react-scene/src/use-signal-visualizer.ts +103 -0
- package/packages/react-scene/tsconfig.json +8 -0
- package/packages/scene/README.md +5 -0
- package/packages/scene/package.json +32 -0
- package/packages/scene/src/index.ts +2 -0
- package/packages/scene/src/room.ts +40 -0
- package/packages/scene/src/transform.ts +60 -0
- package/packages/scene/tsconfig.json +8 -0
- package/packages/signal/README.md +9 -0
- package/packages/signal/package.json +32 -0
- package/packages/signal/src/analyser.ts +113 -0
- package/packages/signal/src/bands.ts +33 -0
- package/packages/signal/src/index.ts +3 -0
- package/packages/signal/src/motion.ts +80 -0
- package/packages/signal/tsconfig.json +8 -0
- package/packages/site/README.md +19 -0
- package/packages/site/package.json +32 -0
- package/packages/site/src/index.ts +8 -0
- package/packages/site/src/open-graph.ts +90 -0
- package/packages/site/tsconfig.json +8 -0
- package/packages/solid/README.md +30 -0
- package/packages/solid/package.json +42 -0
- package/packages/solid/src/hooks/use-aeon-machine.ts +10 -0
- package/packages/solid/src/index.ts +2 -0
- package/packages/solid/src/switch/index.tsx +191 -0
- package/packages/solid/src/utils/merge-props.ts +3 -0
- package/packages/solid/tsconfig.json +10 -0
- package/packages/surface/README.md +10 -0
- package/packages/surface/package.json +33 -0
- package/packages/surface/src/index.ts +1 -0
- package/packages/surface/src/materials.ts +14 -0
- package/packages/surface/surface.css +113 -0
- package/packages/surface/tsconfig.json +8 -0
- package/packages/svelte/README.md +45 -0
- package/packages/svelte/package.json +49 -0
- package/packages/svelte/src/Switch.svelte +86 -0
- package/packages/svelte/src/SwitchControl.svelte +45 -0
- package/packages/svelte/src/SwitchHiddenInput.svelte +12 -0
- package/packages/svelte/src/SwitchLabel.svelte +9 -0
- package/packages/svelte/src/SwitchThumb.svelte +16 -0
- package/packages/svelte/src/hooks/use-aeon-machine.ts +23 -0
- package/packages/svelte/src/index.ts +2 -0
- package/packages/svelte/src/switch-context.ts +13 -0
- package/packages/svelte/tsconfig.json +8 -0
- package/packages/ui/README.md +63 -0
- package/packages/ui/package.json +45 -0
- package/packages/ui/src/accordion.tsx +27 -0
- package/packages/ui/src/async.tsx +43 -0
- package/packages/ui/src/badge.tsx +18 -0
- package/packages/ui/src/button.tsx +38 -0
- package/packages/ui/src/checkbox.tsx +24 -0
- package/packages/ui/src/cn.ts +1 -0
- package/packages/ui/src/combobox.tsx +29 -0
- package/packages/ui/src/dialog.tsx +32 -0
- package/packages/ui/src/field.tsx +33 -0
- package/packages/ui/src/index.ts +17 -0
- package/packages/ui/src/menu.tsx +25 -0
- package/packages/ui/src/popover.tsx +22 -0
- package/packages/ui/src/scroll.tsx +51 -0
- package/packages/ui/src/select.tsx +33 -0
- package/packages/ui/src/styles.css +2 -0
- package/packages/ui/src/switch.tsx +22 -0
- package/packages/ui/src/tabs.tsx +22 -0
- package/packages/ui/src/toast.tsx +36 -0
- package/packages/ui/src/tooltip.tsx +19 -0
- package/packages/ui/tsconfig.json +12 -0
- package/packages/vue/README.md +40 -0
- package/packages/vue/package.json +42 -0
- package/packages/vue/src/hooks/use-aeon-machine.ts +10 -0
- package/packages/vue/src/index.ts +2 -0
- package/packages/vue/src/switch/index.ts +208 -0
- package/packages/vue/src/utils/part-props.ts +8 -0
- package/packages/vue/tsconfig.json +8 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/ensure-headless-built.mjs +25 -0
- package/tsconfig.base.json +21 -0
- package/vercel.json +12 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Vite apps (headless, no Panda)
|
|
2
|
+
|
|
3
|
+
Use this for marketing sites and HandCash apps that keep their own CSS and only need `@aeon-ui/react`.
|
|
4
|
+
|
|
5
|
+
## 1. Install runtime deps
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @xstate/react xstate
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
After `@aeon-ui/*` is on npm, also:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @aeon-ui/react @aeon-ui/core @aeon-ui/primitives
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Until publish, point Vite at a local clone (see below).
|
|
18
|
+
|
|
19
|
+
## 2. Ensure packages are built
|
|
20
|
+
|
|
21
|
+
From the Aeon UI repo:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm run build:headless
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or from any consumer `package.json`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"scripts": {
|
|
32
|
+
"aeon:ensure": "node ../path/to/AeonUI/scripts/ensure-headless-built.mjs",
|
|
33
|
+
"prebuild": "npm run aeon:ensure && vite build"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Set `AEON_UI_ROOT` in CI when the monorepo is not at `~/AeonUI`.
|
|
39
|
+
|
|
40
|
+
## 3. Vite aliases (local clone)
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import path from 'node:path'
|
|
44
|
+
import { fileURLToPath } from 'node:url'
|
|
45
|
+
|
|
46
|
+
const root = path.dirname(fileURLToPath(import.meta.url))
|
|
47
|
+
const aeonRoot = process.env.AEON_UI_ROOT ?? path.resolve(root, '../../../AeonUI')
|
|
48
|
+
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
resolve: {
|
|
51
|
+
alias: {
|
|
52
|
+
'@aeon-ui/core': path.join(aeonRoot, 'packages/core/dist/index.js'),
|
|
53
|
+
'@aeon-ui/primitives': path.join(aeonRoot, 'packages/primitives/dist/index.js'),
|
|
54
|
+
'@aeon-ui/react': path.join(aeonRoot, 'packages/react/dist/index.js'),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
optimizeDeps: {
|
|
58
|
+
include: ['@aeon-ui/react', '@aeon-ui/core', '@aeon-ui/primitives', 'xstate', '@xstate/react'],
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Adjust `../../../AeonUI` to your layout.
|
|
64
|
+
|
|
65
|
+
## 4. TypeScript paths
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"compilerOptions": {
|
|
70
|
+
"paths": {
|
|
71
|
+
"@aeon-ui/core": ["../../../AeonUI/packages/core/dist/index.d.ts"],
|
|
72
|
+
"@aeon-ui/primitives": ["../../../AeonUI/packages/primitives/dist/index.d.ts"],
|
|
73
|
+
"@aeon-ui/react": ["../../../AeonUI/packages/react/dist/index.d.ts"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 5. Style with data-aeon-*
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
[data-aeon-scope='field'][data-aeon-part='control'],
|
|
83
|
+
[data-aeon-scope='field'][data-aeon-part='textarea'] {
|
|
84
|
+
/* shared input styles */
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See [INTEGRATION.md](../INTEGRATION.md) and [STATES.md](../STATES.md).
|
|
89
|
+
|
|
90
|
+
## 6. Toasts
|
|
91
|
+
|
|
92
|
+
Wrap the app (or layout) once:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { Toast } from '@aeon-ui/react'
|
|
96
|
+
|
|
97
|
+
<Toast.Provider>
|
|
98
|
+
<App />
|
|
99
|
+
<Toast.Viewport placement="bottom-end" />
|
|
100
|
+
</Toast.Provider>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Publish from anywhere under the tree:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
const { publish } = useToast()
|
|
107
|
+
publish({ title: 'Saved', description: 'Changes are live.' })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 7. Migrating from Ark UI
|
|
111
|
+
|
|
112
|
+
Full guide: **[MIGRATION_FROM_ARK.md](./MIGRATION_FROM_ARK.md)** (inventory, Next.js, Select/Dialog/Toast/Avatar, CSS selectors, verification).
|
|
113
|
+
|
|
114
|
+
Reference consumers: **spidercorp.xyz** (Vite), **soundbase.click** (Next.js).
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aeon-ui-engine",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "pnpm --filter @aeon-ui/panda run codegen && pnpm -r run build",
|
|
7
|
+
"build:headless": "pnpm --filter @aeon-ui/core --filter @aeon-ui/primitives --filter @aeon-ui/react run build",
|
|
8
|
+
"codegen": "pnpm --filter @aeon-ui/panda codegen",
|
|
9
|
+
"dev": "pnpm --filter @aeon-ui/demo dev",
|
|
10
|
+
"typecheck": "pnpm -r run typecheck",
|
|
11
|
+
"test": "pnpm run test:headless",
|
|
12
|
+
"test:headless": "pnpm run build:headless && pnpm --filter @aeon-ui/core --filter @aeon-ui/primitives --filter @aeon-ui/react run test",
|
|
13
|
+
"changeset": "changeset",
|
|
14
|
+
"version-packages": "changeset version",
|
|
15
|
+
"release": "pnpm build && changeset publish"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@changesets/cli": "^2.29.0",
|
|
19
|
+
"typescript": "^5.8.3"
|
|
20
|
+
},
|
|
21
|
+
"packageManager": "pnpm@10.11.0",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @aeon-ui/core
|
|
2
|
+
|
|
3
|
+
**DOM contract and anatomy** — no React, no XState runtime in components.
|
|
4
|
+
|
|
5
|
+
Agents: read [AGENTS.md](../../AGENTS.md) and [docs/FILE_MAP.md](../../docs/FILE_MAP.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @aeon-ui/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Usually consumed via `@aeon-ui/react` or `@aeon-ui/ui`.
|
|
14
|
+
|
|
15
|
+
## Exports
|
|
16
|
+
|
|
17
|
+
| Module | Purpose |
|
|
18
|
+
|--------|---------|
|
|
19
|
+
| `philosophy` | `UI_IS_FUNCTION_OF_STATE`, `AEON_PRINCIPLE` |
|
|
20
|
+
| `anatomy` | `buttonAnatomy`, `asyncAnatomy`, `fieldAnatomy`, … |
|
|
21
|
+
| `attrs` | `partAttrs`, `scopeAttrs`, `partOnlyAttrs`, `stateToAttr` |
|
|
22
|
+
| `machine` | `createAeonMachine` — XState `setup` helper |
|
|
23
|
+
| `scroll` | `getScrollSnapshot(element)` |
|
|
24
|
+
| `layout` | Normalized coords, `ResponsiveLayoutSpec`, `layoutModeAttrs` |
|
|
25
|
+
| `types` | Shared types |
|
|
26
|
+
|
|
27
|
+
### Play-surface layout (coordinates)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import {
|
|
31
|
+
playSurfaceAnatomy,
|
|
32
|
+
normFromClient,
|
|
33
|
+
layoutModeAttrs,
|
|
34
|
+
type ResponsiveLayoutSpec,
|
|
35
|
+
} from '@aeon-ui/core'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
See [docs/LAYOUT_COORDINATES.md](../../docs/LAYOUT_COORDINATES.md).
|
|
39
|
+
|
|
40
|
+
## Anatomy
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { asyncAnatomy, partAttrs } from '@aeon-ui/core'
|
|
44
|
+
|
|
45
|
+
partAttrs(asyncAnatomy.scope, asyncAnatomy.readout, {
|
|
46
|
+
state: 'loading',
|
|
47
|
+
})
|
|
48
|
+
// → data-aeon-scope="async" data-aeon-part="readout" data-aeon-state="loading"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Parallel regions
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { stateToAttr } from '@aeon-ui/core'
|
|
55
|
+
|
|
56
|
+
stateToAttr({ interaction: 'dirty', validation: 'invalid', submission: 'idle' })
|
|
57
|
+
// → "interaction:dirty validation:invalid submission:idle"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Adding a scope
|
|
61
|
+
|
|
62
|
+
1. Add `myAnatomy = anatomy('my', ['root', ...] as const)` in `src/anatomy.ts`.
|
|
63
|
+
2. Use in React via `partAttrs`.
|
|
64
|
+
3. Follow [docs/COMPONENT_CHECKLIST.md](../../docs/COMPONENT_CHECKLIST.md).
|
|
65
|
+
|
|
66
|
+
## Related
|
|
67
|
+
|
|
68
|
+
- [STATES.md](../../STATES.md) — attribute contract
|
|
69
|
+
- [packages/primitives/README.md](../primitives/README.md) — machines
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aeon-ui/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Anatomy tokens, data-aeon attributes, and machine helpers for Aeon UI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/GenericCPU/aeon-ui.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": { "access": "public" },
|
|
12
|
+
"files": ["dist", "theme-transition.css", "README.md"],
|
|
13
|
+
"keywords": ["aeon-ui", "statechart", "xstate", "headless", "anatomy"],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./theme-transition.css": "./theme-transition.css"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"xstate": "^5.19.2"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"tsup": "^8.4.0",
|
|
34
|
+
"typescript": "^5.8.3",
|
|
35
|
+
"vitest": "^3.2.4"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/** Part keys for compound components — scope + named parts for styling and docs. */
|
|
2
|
+
export function anatomy<const T extends readonly string[]>(scope: string, parts: T) {
|
|
3
|
+
return Object.fromEntries(parts.map((p) => [p, p])) as Record<T[number], T[number]> & {
|
|
4
|
+
scope: typeof scope
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const buttonAnatomy = anatomy('button', [
|
|
9
|
+
'root',
|
|
10
|
+
'group',
|
|
11
|
+
'label',
|
|
12
|
+
'icon',
|
|
13
|
+
] as const)
|
|
14
|
+
|
|
15
|
+
export const switchAnatomy = anatomy('switch', [
|
|
16
|
+
'root',
|
|
17
|
+
'control',
|
|
18
|
+
'thumb',
|
|
19
|
+
'label',
|
|
20
|
+
'hiddenInput',
|
|
21
|
+
] as const)
|
|
22
|
+
|
|
23
|
+
export const checkboxAnatomy = anatomy('checkbox', [
|
|
24
|
+
'root',
|
|
25
|
+
'control',
|
|
26
|
+
'indicator',
|
|
27
|
+
'label',
|
|
28
|
+
'hiddenInput',
|
|
29
|
+
] as const)
|
|
30
|
+
|
|
31
|
+
export const dialogAnatomy = anatomy('dialog', [
|
|
32
|
+
'root',
|
|
33
|
+
'trigger',
|
|
34
|
+
'portal',
|
|
35
|
+
'backdrop',
|
|
36
|
+
'positioner',
|
|
37
|
+
'content',
|
|
38
|
+
'title',
|
|
39
|
+
'description',
|
|
40
|
+
'closeTrigger',
|
|
41
|
+
] as const)
|
|
42
|
+
|
|
43
|
+
export const tabsAnatomy = anatomy('tabs', [
|
|
44
|
+
'root',
|
|
45
|
+
'list',
|
|
46
|
+
'trigger',
|
|
47
|
+
'content',
|
|
48
|
+
'indicator',
|
|
49
|
+
] as const)
|
|
50
|
+
|
|
51
|
+
export const accordionAnatomy = anatomy('accordion', [
|
|
52
|
+
'root',
|
|
53
|
+
'item',
|
|
54
|
+
'itemTrigger',
|
|
55
|
+
'itemContent',
|
|
56
|
+
'itemIndicator',
|
|
57
|
+
] as const)
|
|
58
|
+
|
|
59
|
+
/** Async data region — track shows every lifecycle state; readout + actions mirror snapshot. */
|
|
60
|
+
export const asyncAnatomy = anatomy('async', [
|
|
61
|
+
'root',
|
|
62
|
+
'track',
|
|
63
|
+
'segment',
|
|
64
|
+
'readout',
|
|
65
|
+
'readoutRail',
|
|
66
|
+
'readoutBody',
|
|
67
|
+
'actions',
|
|
68
|
+
] as const)
|
|
69
|
+
|
|
70
|
+
/** Form field — orthogonal interaction × validation × submission (see STATES.md). */
|
|
71
|
+
export const fieldAnatomy = anatomy('field', [
|
|
72
|
+
'root',
|
|
73
|
+
'label',
|
|
74
|
+
'control',
|
|
75
|
+
'textarea',
|
|
76
|
+
'message',
|
|
77
|
+
'hint',
|
|
78
|
+
] as const)
|
|
79
|
+
|
|
80
|
+
/** Scroll viewport — overflow axis + edge position on data-aeon-state. */
|
|
81
|
+
export const scrollAnatomy = anatomy('scroll', ['root', 'viewport', 'content'] as const)
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Play / game surface — mat shell with table, dice, and bank regions.
|
|
85
|
+
* Use with normalized layout coords (@see docs/LAYOUT_COORDINATES.md).
|
|
86
|
+
*/
|
|
87
|
+
export const playSurfaceAnatomy = anatomy('play', ['mat', 'table', 'dice', 'bank'] as const)
|
|
88
|
+
|
|
89
|
+
export const selectAnatomy = anatomy('select', [
|
|
90
|
+
'root',
|
|
91
|
+
'trigger',
|
|
92
|
+
'value',
|
|
93
|
+
'icon',
|
|
94
|
+
'positioner',
|
|
95
|
+
'content',
|
|
96
|
+
'item',
|
|
97
|
+
] as const)
|
|
98
|
+
|
|
99
|
+
/** Typeahead listbox — extends select with filter input and empty state. */
|
|
100
|
+
export const comboboxAnatomy = anatomy('combobox', [
|
|
101
|
+
'root',
|
|
102
|
+
'input',
|
|
103
|
+
'icon',
|
|
104
|
+
'content',
|
|
105
|
+
'item',
|
|
106
|
+
'empty',
|
|
107
|
+
] as const)
|
|
108
|
+
|
|
109
|
+
export const popoverAnatomy = anatomy('popover', [
|
|
110
|
+
'root',
|
|
111
|
+
'trigger',
|
|
112
|
+
'positioner',
|
|
113
|
+
'content',
|
|
114
|
+
'arrow',
|
|
115
|
+
'closeTrigger',
|
|
116
|
+
] as const)
|
|
117
|
+
|
|
118
|
+
export const menuAnatomy = anatomy('menu', [
|
|
119
|
+
'root',
|
|
120
|
+
'trigger',
|
|
121
|
+
'positioner',
|
|
122
|
+
'content',
|
|
123
|
+
'item',
|
|
124
|
+
'separator',
|
|
125
|
+
] as const)
|
|
126
|
+
|
|
127
|
+
export const tooltipAnatomy = anatomy('tooltip', [
|
|
128
|
+
'root',
|
|
129
|
+
'trigger',
|
|
130
|
+
'positioner',
|
|
131
|
+
'content',
|
|
132
|
+
'arrow',
|
|
133
|
+
] as const)
|
|
134
|
+
|
|
135
|
+
export const toastAnatomy = anatomy('toast', [
|
|
136
|
+
'viewport',
|
|
137
|
+
'root',
|
|
138
|
+
'title',
|
|
139
|
+
'description',
|
|
140
|
+
'closeTrigger',
|
|
141
|
+
] as const)
|
|
142
|
+
|
|
143
|
+
/** Profile image — loaded / loading / error on root `data-aeon-state`. */
|
|
144
|
+
export const avatarAnatomy = anatomy('avatar', ['root', 'image', 'fallback'] as const)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type AttrOptions = {
|
|
2
|
+
state?: string | string[]
|
|
3
|
+
disabled?: boolean
|
|
4
|
+
focus?: boolean
|
|
5
|
+
highlighted?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function stateFields(options?: AttrOptions) {
|
|
9
|
+
const states = options?.state
|
|
10
|
+
? Array.isArray(options.state)
|
|
11
|
+
? options.state.join(' ')
|
|
12
|
+
: options.state
|
|
13
|
+
: undefined
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
...(states ? { 'data-aeon-state': states } : {}),
|
|
17
|
+
...(options?.disabled ? { 'data-disabled': '' } : {}),
|
|
18
|
+
...(options?.focus ? { 'data-focus': '' } : {}),
|
|
19
|
+
...(options?.highlighted ? { 'data-highlighted': '' } : {}),
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Scope + part on the component root (e.g. accordion root). */
|
|
24
|
+
export function scopeAttrs(
|
|
25
|
+
scope: string,
|
|
26
|
+
part = 'root',
|
|
27
|
+
options?: AttrOptions,
|
|
28
|
+
): Record<string, unknown> {
|
|
29
|
+
return {
|
|
30
|
+
'data-aeon-scope': scope,
|
|
31
|
+
'data-aeon-part': part,
|
|
32
|
+
...stateFields(options),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Part only — use on children inside a scoped root. */
|
|
37
|
+
export function partOnlyAttrs(part: string, options?: AttrOptions): Record<string, unknown> {
|
|
38
|
+
return {
|
|
39
|
+
'data-aeon-part': part,
|
|
40
|
+
...stateFields(options),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Build data-aeon-* attributes for styling unstyled parts. */
|
|
45
|
+
export function partAttrs(scope: string, part: string, options?: AttrOptions): Record<string, unknown> {
|
|
46
|
+
return scopeAttrs(scope, part, options)
|
|
47
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responsive play-surface layout — logical coordinate plane + composition modes.
|
|
3
|
+
*
|
|
4
|
+
* Behavior stays in statecharts; **where** the user points is expressed in normalized
|
|
5
|
+
* coordinates (0–100) inside a measured region, independent of CSS grid vs overlay.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/LAYOUT_COORDINATES.md
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Point on the normalized plane (percent of region width/height). */
|
|
11
|
+
export type NormPoint = { readonly x: number; readonly y: number }
|
|
12
|
+
|
|
13
|
+
/** Axis-aligned bounds on the normalized plane (inclusive). */
|
|
14
|
+
export type NormRect = {
|
|
15
|
+
readonly xMin: number
|
|
16
|
+
readonly xMax: number
|
|
17
|
+
readonly yMin: number
|
|
18
|
+
readonly yMax: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** DOM mapping: y increases upward (throws, dice) vs downward (top-left UI). */
|
|
22
|
+
export type NormOrigin = 'bottom-left' | 'top-left'
|
|
23
|
+
|
|
24
|
+
export const NORM_PLANE = {
|
|
25
|
+
bottomLeft: 'bottom-left',
|
|
26
|
+
topLeft: 'top-left',
|
|
27
|
+
} as const satisfies Record<string, NormOrigin>
|
|
28
|
+
|
|
29
|
+
/** How regions are composed at a breakpoint — product maps to CSS grid / absolute. */
|
|
30
|
+
export type LayoutComposition = 'split' | 'stack' | 'overlay'
|
|
31
|
+
|
|
32
|
+
export interface RegionLayoutSlot {
|
|
33
|
+
/** Logical region id (matches anatomy part or app constant). */
|
|
34
|
+
readonly region: string
|
|
35
|
+
/** Grid area name / slot id for CSS `grid-template-areas`. */
|
|
36
|
+
readonly slot: string
|
|
37
|
+
readonly zIndex?: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface LayoutCompositionSpec {
|
|
41
|
+
readonly id: string
|
|
42
|
+
readonly composition: LayoutComposition
|
|
43
|
+
readonly regions: readonly RegionLayoutSlot[]
|
|
44
|
+
/** Interaction bounds per region in **that region's** normalized plane. */
|
|
45
|
+
readonly bounds?: Readonly<Record<string, NormRect>>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type LayoutMode = 'compact' | 'wide'
|
|
49
|
+
|
|
50
|
+
export interface ResponsiveLayoutSpec {
|
|
51
|
+
readonly modes: {
|
|
52
|
+
readonly compact: LayoutCompositionSpec
|
|
53
|
+
readonly wide: LayoutCompositionSpec
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Media query that selects `compact` when matched (default: phone portrait).
|
|
57
|
+
* When it does not match, mode is `wide`.
|
|
58
|
+
*/
|
|
59
|
+
readonly compactMq?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const DEFAULT_COMPACT_MQ = '(max-width: 39.99rem)'
|
|
63
|
+
|
|
64
|
+
export function clampNorm(point: NormPoint, bounds: NormRect): NormPoint {
|
|
65
|
+
return {
|
|
66
|
+
x: Math.min(bounds.xMax, Math.max(bounds.xMin, point.x)),
|
|
67
|
+
y: Math.min(bounds.yMax, Math.max(bounds.yMin, point.y)),
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function normFromClient(
|
|
72
|
+
rect: DOMRectReadOnly,
|
|
73
|
+
clientX: number,
|
|
74
|
+
clientY: number,
|
|
75
|
+
options?: { origin?: NormOrigin; bounds?: NormRect },
|
|
76
|
+
): NormPoint {
|
|
77
|
+
const origin = options?.origin ?? NORM_PLANE.bottomLeft
|
|
78
|
+
const w = rect.width || 1
|
|
79
|
+
const h = rect.height || 1
|
|
80
|
+
const x = ((clientX - rect.left) / w) * 100
|
|
81
|
+
const yFromTop = (clientY - rect.top) / h
|
|
82
|
+
const y =
|
|
83
|
+
origin === NORM_PLANE.bottomLeft ? (1 - yFromTop) * 100 : yFromTop * 100
|
|
84
|
+
const raw = { x, y }
|
|
85
|
+
return options?.bounds ? clampNorm(raw, options.bounds) : raw
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function clientFromNorm(
|
|
89
|
+
rect: DOMRectReadOnly,
|
|
90
|
+
point: NormPoint,
|
|
91
|
+
options?: { origin?: NormOrigin },
|
|
92
|
+
): { clientX: number; clientY: number } {
|
|
93
|
+
const origin = options?.origin ?? NORM_PLANE.bottomLeft
|
|
94
|
+
const w = rect.width || 1
|
|
95
|
+
const h = rect.height || 1
|
|
96
|
+
const clientX = rect.left + (point.x / 100) * w
|
|
97
|
+
const yFromTop = origin === NORM_PLANE.bottomLeft ? 1 - point.y / 100 : point.y / 100
|
|
98
|
+
const clientY = rect.top + yFromTop * h
|
|
99
|
+
return { clientX, clientY }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Measure element; returns null if not connected. */
|
|
103
|
+
export function measureElement(el: Element | null | undefined): DOMRect | null {
|
|
104
|
+
if (!el || !('getBoundingClientRect' in el)) return null
|
|
105
|
+
const rect = el.getBoundingClientRect()
|
|
106
|
+
return rect.width > 0 && rect.height > 0 ? rect : null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Prefer a dedicated region rect; fall back to mat when lane not mounted. */
|
|
110
|
+
export function resolveRegionRect(
|
|
111
|
+
primary: DOMRect | null | undefined,
|
|
112
|
+
fallback: DOMRect | null | undefined,
|
|
113
|
+
): DOMRect | null {
|
|
114
|
+
if (primary && primary.width > 0 && primary.height > 0) return primary
|
|
115
|
+
if (fallback && fallback.width > 0 && fallback.height > 0) return fallback
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function matchLayoutMode(
|
|
120
|
+
spec: ResponsiveLayoutSpec,
|
|
121
|
+
mq: Pick<MediaQueryList, 'matches'> = getCompactMq(spec),
|
|
122
|
+
): LayoutMode {
|
|
123
|
+
return mq.matches ? 'compact' : 'wide'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function getCompactMq(spec: ResponsiveLayoutSpec): MediaQueryList {
|
|
127
|
+
const query = spec.compactMq ?? DEFAULT_COMPACT_MQ
|
|
128
|
+
if (typeof window === 'undefined' || !window.matchMedia) {
|
|
129
|
+
return { matches: false } as MediaQueryList
|
|
130
|
+
}
|
|
131
|
+
return window.matchMedia(query)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function layoutCompositionForMode(
|
|
135
|
+
spec: ResponsiveLayoutSpec,
|
|
136
|
+
mode: LayoutMode,
|
|
137
|
+
): LayoutCompositionSpec {
|
|
138
|
+
return spec.modes[mode]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function regionBoundsForMode(
|
|
142
|
+
spec: ResponsiveLayoutSpec,
|
|
143
|
+
mode: LayoutMode,
|
|
144
|
+
regionId: string,
|
|
145
|
+
): NormRect | undefined {
|
|
146
|
+
return spec.modes[mode].bounds?.[regionId]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Value for `data-aeon-layout-mode` on the mat root.
|
|
151
|
+
* Pair with `data-aeon-layout-composition` from the active spec.
|
|
152
|
+
*/
|
|
153
|
+
export function layoutModeAttrs(mode: LayoutMode, composition: LayoutComposition): Record<string, string> {
|
|
154
|
+
return {
|
|
155
|
+
'data-aeon-layout-mode': mode,
|
|
156
|
+
'data-aeon-layout-composition': composition,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Spread a norm point inside bounds (keeps cluster off edges). */
|
|
161
|
+
export function insetNormCluster(
|
|
162
|
+
point: NormPoint,
|
|
163
|
+
bounds: NormRect,
|
|
164
|
+
inset: { x?: number; y?: number } = {},
|
|
165
|
+
): NormPoint {
|
|
166
|
+
const padX = inset.x ?? 0
|
|
167
|
+
const padY = inset.y ?? 0
|
|
168
|
+
const halfX = (bounds.xMax - bounds.xMin) / 2
|
|
169
|
+
const halfY = (bounds.yMax - bounds.yMin) / 2
|
|
170
|
+
const capX = Math.min(padX, halfX - 0.5)
|
|
171
|
+
const capY = Math.min(padY, halfY - 0.5)
|
|
172
|
+
return {
|
|
173
|
+
x: Math.min(bounds.xMax - capX, Math.max(bounds.xMin + capX, point.x)),
|
|
174
|
+
y: Math.min(bounds.yMax - capY, Math.max(bounds.yMin + capY, point.y)),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { stateToAttr } from './machine.js'
|
|
3
|
+
|
|
4
|
+
describe('stateToAttr', () => {
|
|
5
|
+
it('stringifies string states', () => {
|
|
6
|
+
expect(stateToAttr('open')).toBe('open')
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('flattens parallel regions', () => {
|
|
10
|
+
expect(
|
|
11
|
+
stateToAttr({
|
|
12
|
+
interaction: 'dirty',
|
|
13
|
+
validation: 'invalid',
|
|
14
|
+
submission: 'idle',
|
|
15
|
+
}),
|
|
16
|
+
).toBe('interaction:dirty validation:invalid submission:idle')
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { setup, type ActorRefFrom, type AnyStateMachine, type SnapshotFrom } from 'xstate'
|
|
2
|
+
|
|
3
|
+
/** Re-export XState setup — encode UI behavior as executable statecharts. */
|
|
4
|
+
export { setup as createAeonMachine }
|
|
5
|
+
|
|
6
|
+
export type AeonSnapshot<M extends AnyStateMachine> = SnapshotFrom<M>
|
|
7
|
+
export type AeonActor<M extends AnyStateMachine> = ActorRefFrom<M>
|
|
8
|
+
|
|
9
|
+
/** Flatten nested state value for data-aeon-state attribute. */
|
|
10
|
+
export function stateToAttr(value: unknown): string {
|
|
11
|
+
if (value == null || typeof value === 'string') return String(value ?? '')
|
|
12
|
+
return Object.entries(value as Record<string, unknown>)
|
|
13
|
+
.map(([k, v]) => `${k}:${stateToAttr(v)}`)
|
|
14
|
+
.join(' ')
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aeon north star: behavior is defined as explicit state first; the interface must
|
|
3
|
+
* express the **totality** of that definition — every stable condition the model allows,
|
|
4
|
+
* and nothing the model does not allow.
|
|
5
|
+
*/
|
|
6
|
+
export const AEON_PRINCIPLE =
|
|
7
|
+
'State is defined first. UI represents the totality of that definition.' as const
|
|
8
|
+
|
|
9
|
+
/** Shorthand used in docs and APIs. */
|
|
10
|
+
export const UI_IS_FUNCTION_OF_STATE = 'UI = f(state)' as const
|