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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/static-data-slot-scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev-ui": minor
---

Data Slot components start with their page script, with no loading helper. Dialog, Sheet and NavigationMenu no longer load their controller per media query: `media` is still accepted but has no effect, and the registry no longer has the `on-media` item or `src/lib/dialog.ts`; a site that installed them can delete `src/lib/on-media.ts` and `src/lib/dialog.ts` once nothing imports them. Header blocks 1 to 5 take `fold="lg"`, the default, or `fold="xl"` for the breakpoint between their mobile sheet and desktop navigation, and close whichever one the layout hides at that breakpoint, which also releases the sheet's scroll lock. Every Data Slot component also binds new roots on `astro:after-swap`, so a site's own `astro:page-load` command after ClientRouter navigation reaches a started controller.
2 changes: 1 addition & 1 deletion public/r/styles/base-luma/accordion.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"files": [
{
"path": "src/components/ui/accordion/accordion.astro",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n multiple?: boolean\n defaultValue?: string | string[]\n disabled?: boolean\n orientation?: \"horizontal\" | \"vertical\"\n loopFocus?: boolean\n hiddenUntilFound?: boolean\n collapsible?: boolean\n}\n\nconst {\n class: className,\n multiple,\n defaultValue,\n disabled,\n orientation = \"vertical\",\n loopFocus,\n hiddenUntilFound,\n collapsible,\n ...props\n} = Astro.props\n\nconst serializedDefaultValue = Array.isArray(defaultValue)\n ? JSON.stringify(defaultValue)\n : defaultValue\n---\n\n<div\n data-slot=\"accordion\"\n data-multiple={multiple}\n data-default-value={serializedDefaultValue}\n data-disabled={disabled}\n data-orientation={orientation}\n data-loop-focus={loopFocus}\n data-hidden-until-found={hiddenUntilFound}\n data-collapsible={collapsible}\n class={cn(\"flex w-full flex-col\", className)}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/accordion\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n multiple?: boolean\n defaultValue?: string | string[]\n disabled?: boolean\n orientation?: \"horizontal\" | \"vertical\"\n loopFocus?: boolean\n hiddenUntilFound?: boolean\n collapsible?: boolean\n}\n\nconst {\n class: className,\n multiple,\n defaultValue,\n disabled,\n orientation = \"vertical\",\n loopFocus,\n hiddenUntilFound,\n collapsible,\n ...props\n} = Astro.props\n\nconst serializedDefaultValue = Array.isArray(defaultValue)\n ? JSON.stringify(defaultValue)\n : defaultValue\n---\n\n<div\n data-slot=\"accordion\"\n data-multiple={multiple}\n data-default-value={serializedDefaultValue}\n data-disabled={disabled}\n data-orientation={orientation}\n data-loop-focus={loopFocus}\n data-hidden-until-found={hiddenUntilFound}\n data-collapsible={collapsible}\n class={cn(\"flex w-full flex-col\", className)}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/accordion\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:after-swap\", initialize)\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"type": "registry:ui"
},
{
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/base-luma/alert-dialog.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"files": [
{
"path": "src/components/ui/alert-dialog/alert-dialog.astro",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n defaultOpen?: boolean\n closeOnClickOutside?: boolean\n closeOnEscape?: boolean\n lockScroll?: boolean\n}\n\nconst {\n class: className,\n defaultOpen,\n closeOnClickOutside,\n closeOnEscape,\n lockScroll,\n ...props\n} = Astro.props\n---\n\n<div\n data-slot=\"alert-dialog\"\n data-default-open={defaultOpen}\n data-close-on-click-outside={closeOnClickOutside}\n data-close-on-escape={closeOnEscape}\n data-lock-scroll={lockScroll}\n class={className}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/alert-dialog\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n defaultOpen?: boolean\n closeOnClickOutside?: boolean\n closeOnEscape?: boolean\n lockScroll?: boolean\n}\n\nconst {\n class: className,\n defaultOpen,\n closeOnClickOutside,\n closeOnEscape,\n lockScroll,\n ...props\n} = Astro.props\n---\n\n<div\n data-slot=\"alert-dialog\"\n data-default-open={defaultOpen}\n data-close-on-click-outside={closeOnClickOutside}\n data-close-on-escape={closeOnEscape}\n data-lock-scroll={lockScroll}\n class={className}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/alert-dialog\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:after-swap\", initialize)\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"type": "registry:ui"
},
{
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/base-luma/collapsible.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"files": [
{
"path": "src/components/ui/collapsible/collapsible.astro",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n // \"li\" makes a list item the collapsible, such as a sidebar menu item.\n as?: \"div\" | \"li\"\n defaultOpen?: boolean\n hiddenUntilFound?: boolean\n closeOnEscape?: boolean\n closeOnClickOutside?: boolean\n}\n\nconst {\n as: Tag = \"div\",\n class: className,\n defaultOpen,\n hiddenUntilFound,\n closeOnEscape,\n closeOnClickOutside,\n ...props\n} = Astro.props\n---\n\n<Tag\n data-slot=\"collapsible\"\n data-default-open={defaultOpen}\n data-hidden-until-found={hiddenUntilFound}\n data-close-on-escape={closeOnEscape}\n data-close-on-click-outside={closeOnClickOutside}\n class={cn(className)}\n {...props}\n>\n <slot />\n</Tag>\n\n<script>\n import { create } from \"@data-slot/collapsible\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n // \"li\" makes a list item the collapsible, such as a sidebar menu item.\n as?: \"div\" | \"li\"\n defaultOpen?: boolean\n hiddenUntilFound?: boolean\n closeOnEscape?: boolean\n closeOnClickOutside?: boolean\n}\n\nconst {\n as: Tag = \"div\",\n class: className,\n defaultOpen,\n hiddenUntilFound,\n closeOnEscape,\n closeOnClickOutside,\n ...props\n} = Astro.props\n---\n\n<Tag\n data-slot=\"collapsible\"\n data-default-open={defaultOpen}\n data-hidden-until-found={hiddenUntilFound}\n data-close-on-escape={closeOnEscape}\n data-close-on-click-outside={closeOnClickOutside}\n class={cn(className)}\n {...props}\n>\n <slot />\n</Tag>\n\n<script>\n import { create } from \"@data-slot/collapsible\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:after-swap\", initialize)\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"type": "registry:ui"
},
{
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/base-luma/combobox.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"files": [
{
"path": "src/components/ui/combobox/combobox.astro",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\nimport type {\n Align as ComboboxAlign,\n Side as ComboboxSide,\n} from \"@data-slot/combobox\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n defaultValue?: string\n defaultOpen?: boolean\n placeholder?: string\n disabled?: boolean\n required?: boolean\n name?: string\n openOnFocus?: boolean\n autoHighlight?: boolean\n side?: ComboboxSide\n align?: ComboboxAlign\n sideOffset?: number\n alignOffset?: number\n avoidCollisions?: boolean\n collisionPadding?: number\n}\n\nconst {\n class: className,\n defaultValue,\n defaultOpen,\n placeholder,\n disabled,\n required,\n name,\n openOnFocus,\n autoHighlight,\n side,\n align,\n sideOffset,\n alignOffset,\n avoidCollisions,\n collisionPadding,\n ...props\n} = Astro.props\n---\n\n<div\n data-slot=\"combobox\"\n data-default-value={defaultValue}\n data-default-open={defaultOpen}\n data-placeholder={placeholder}\n data-disabled={disabled}\n data-required={required}\n data-name={name}\n data-open-on-focus={openOnFocus}\n data-auto-highlight={autoHighlight}\n data-side={side}\n data-align={align}\n data-side-offset={sideOffset}\n data-align-offset={alignOffset}\n data-avoid-collisions={avoidCollisions}\n data-collision-padding={collisionPadding}\n class={cn(\"group/combobox relative\", className)}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/combobox\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\nimport type {\n Align as ComboboxAlign,\n Side as ComboboxSide,\n} from \"@data-slot/combobox\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n defaultValue?: string\n defaultOpen?: boolean\n placeholder?: string\n disabled?: boolean\n required?: boolean\n name?: string\n openOnFocus?: boolean\n autoHighlight?: boolean\n side?: ComboboxSide\n align?: ComboboxAlign\n sideOffset?: number\n alignOffset?: number\n avoidCollisions?: boolean\n collisionPadding?: number\n}\n\nconst {\n class: className,\n defaultValue,\n defaultOpen,\n placeholder,\n disabled,\n required,\n name,\n openOnFocus,\n autoHighlight,\n side,\n align,\n sideOffset,\n alignOffset,\n avoidCollisions,\n collisionPadding,\n ...props\n} = Astro.props\n---\n\n<div\n data-slot=\"combobox\"\n data-default-value={defaultValue}\n data-default-open={defaultOpen}\n data-placeholder={placeholder}\n data-disabled={disabled}\n data-required={required}\n data-name={name}\n data-open-on-focus={openOnFocus}\n data-auto-highlight={autoHighlight}\n data-side={side}\n data-align={align}\n data-side-offset={sideOffset}\n data-align-offset={alignOffset}\n data-avoid-collisions={avoidCollisions}\n data-collision-padding={collisionPadding}\n class={cn(\"group/combobox relative\", className)}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/combobox\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:after-swap\", initialize)\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n</script>\n",
"type": "registry:ui"
},
{
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/base-luma/command.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"files": [
{
"path": "src/components/ui/command/command.astro",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n label?: string\n defaultValue?: string\n defaultSearch?: string\n shouldFilter?: boolean\n loop?: boolean\n disablePointerSelection?: boolean\n vimBindings?: boolean\n}\n\nconst {\n class: className,\n label,\n defaultValue,\n defaultSearch,\n shouldFilter,\n loop,\n disablePointerSelection,\n vimBindings,\n ...props\n} = Astro.props\n---\n\n<div\n data-slot=\"command\"\n data-label={label}\n data-default-value={defaultValue}\n data-default-search={defaultSearch}\n data-should-filter={shouldFilter}\n data-loop={loop}\n data-disable-pointer-selection={disablePointerSelection}\n data-vim-bindings={vimBindings}\n class={cn(\"bg-popover text-popover-foreground rounded-4xl p-1 flex size-full flex-col overflow-hidden\", className)}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/command\"\n import { getParts } from \"@data-slot/core\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n\n const isCommandInput = (\n target: EventTarget | null\n ): target is HTMLInputElement =>\n target instanceof HTMLInputElement &&\n target.dataset.slot === \"command-input\"\n\n // Do not act on the Enter that confirms an IME composition. Safari fires\n // that keydown right after compositionend with isComposing set to false, so\n // treat the first keydown after compositionend as the confirming key when it\n // arrives within 50 ms. Any later keydown is a new key press.\n let composing = false\n let compositionEndedAt = -Infinity\n document.addEventListener(\n \"compositionstart\",\n (event) => {\n if (isCommandInput(event.target)) composing = true\n },\n true\n )\n document.addEventListener(\n \"compositionend\",\n (event) => {\n if (!isCommandInput(event.target)) return\n composing = false\n compositionEndedAt = event.timeStamp\n },\n true\n )\n const confirmsComposition = (event: KeyboardEvent) => {\n if (event.isComposing || composing) return true\n const confirming = event.timeStamp - compositionEndedAt < 50\n compositionEndedAt = -Infinity\n return confirming\n }\n\n // Data Slot selection does not activate links. Preserve native navigation.\n document.addEventListener(\n \"keydown\",\n (event) => {\n const input = event.target\n if (!isCommandInput(input)) return\n const confirming = confirmsComposition(event)\n if (event.key !== \"Enter\" || event.defaultPrevented || confirming) return\n const root = input.closest('[data-slot=\"command\"]')\n if (!root) return\n const item = getParts<HTMLAnchorElement>(root, \"command-item\").find(\n (item) =>\n item.hasAttribute(\"data-selected\") &&\n item.hasAttribute(\"href\") &&\n item.getAttribute(\"aria-disabled\") !== \"true\"\n )\n if (!item) return\n event.preventDefault()\n item.click()\n },\n true\n )\n</script>\n",
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n label?: string\n defaultValue?: string\n defaultSearch?: string\n shouldFilter?: boolean\n loop?: boolean\n disablePointerSelection?: boolean\n vimBindings?: boolean\n}\n\nconst {\n class: className,\n label,\n defaultValue,\n defaultSearch,\n shouldFilter,\n loop,\n disablePointerSelection,\n vimBindings,\n ...props\n} = Astro.props\n---\n\n<div\n data-slot=\"command\"\n data-label={label}\n data-default-value={defaultValue}\n data-default-search={defaultSearch}\n data-should-filter={shouldFilter}\n data-loop={loop}\n data-disable-pointer-selection={disablePointerSelection}\n data-vim-bindings={vimBindings}\n class={cn(\"bg-popover text-popover-foreground rounded-4xl p-1 flex size-full flex-col overflow-hidden\", className)}\n {...props}\n>\n <slot />\n</div>\n\n<script>\n import { create } from \"@data-slot/command\"\n import { getParts } from \"@data-slot/core\"\n\n const controllers: ReturnType<typeof create> = []\n const initialize = () => controllers.push(...create())\n initialize()\n document.addEventListener(\"astro:after-swap\", initialize)\n document.addEventListener(\"astro:page-load\", initialize)\n document.addEventListener(\"astro:before-swap\", () => {\n for (const controller of controllers) controller.destroy()\n controllers.length = 0\n })\n\n const isCommandInput = (\n target: EventTarget | null\n ): target is HTMLInputElement =>\n target instanceof HTMLInputElement &&\n target.dataset.slot === \"command-input\"\n\n // Do not act on the Enter that confirms an IME composition. Safari fires\n // that keydown right after compositionend with isComposing set to false, so\n // treat the first keydown after compositionend as the confirming key when it\n // arrives within 50 ms. Any later keydown is a new key press.\n let composing = false\n let compositionEndedAt = -Infinity\n document.addEventListener(\n \"compositionstart\",\n (event) => {\n if (isCommandInput(event.target)) composing = true\n },\n true\n )\n document.addEventListener(\n \"compositionend\",\n (event) => {\n if (!isCommandInput(event.target)) return\n composing = false\n compositionEndedAt = event.timeStamp\n },\n true\n )\n const confirmsComposition = (event: KeyboardEvent) => {\n if (event.isComposing || composing) return true\n const confirming = event.timeStamp - compositionEndedAt < 50\n compositionEndedAt = -Infinity\n return confirming\n }\n\n // Data Slot selection does not activate links. Preserve native navigation.\n document.addEventListener(\n \"keydown\",\n (event) => {\n const input = event.target\n if (!isCommandInput(input)) return\n const confirming = confirmsComposition(event)\n if (event.key !== \"Enter\" || event.defaultPrevented || confirming) return\n const root = input.closest('[data-slot=\"command\"]')\n if (!root) return\n const item = getParts<HTMLAnchorElement>(root, \"command-item\").find(\n (item) =>\n item.hasAttribute(\"data-selected\") &&\n item.hasAttribute(\"href\") &&\n item.getAttribute(\"aria-disabled\") !== \"true\"\n )\n if (!item) return\n event.preventDefault()\n item.click()\n },\n true\n )\n</script>\n",
"type": "registry:ui"
},
{
Expand Down
Loading