ember-primitives 0.61.1 → 0.63.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.
Files changed (93) hide show
  1. package/README.md +8 -1
  2. package/declarations/components/accordion/content.d.ts +2 -1
  3. package/declarations/components/accordion/content.d.ts.map +1 -1
  4. package/declarations/components/accordion/header.d.ts +2 -1
  5. package/declarations/components/accordion/header.d.ts.map +1 -1
  6. package/declarations/components/accordion/item.d.ts +7 -0
  7. package/declarations/components/accordion/item.d.ts.map +1 -1
  8. package/declarations/components/accordion/public.d.ts +3 -3
  9. package/declarations/components/accordion/public.d.ts.map +1 -1
  10. package/declarations/components/accordion/trigger.d.ts +2 -1
  11. package/declarations/components/accordion/trigger.d.ts.map +1 -1
  12. package/declarations/components/command-palette.d.ts +329 -0
  13. package/declarations/components/command-palette.d.ts.map +1 -0
  14. package/declarations/components/dialog.d.ts +65 -7
  15. package/declarations/components/dialog.d.ts.map +1 -1
  16. package/declarations/components/drawer.d.ts +14 -6
  17. package/declarations/components/drawer.d.ts.map +1 -1
  18. package/declarations/components/link.d.ts.map +1 -1
  19. package/declarations/components/menu.d.ts.map +1 -1
  20. package/declarations/components/one-time-password/otp.d.ts +1 -1
  21. package/declarations/components/one-time-password/utils.d.ts.map +1 -1
  22. package/declarations/components/rating/range.d.ts +1 -0
  23. package/declarations/components/rating/range.d.ts.map +1 -1
  24. package/declarations/components/rating/rating.d.ts +1 -1
  25. package/declarations/components/rating/rating.d.ts.map +1 -1
  26. package/declarations/components/rating/stars.d.ts.map +1 -1
  27. package/declarations/components/rating/state.d.ts +1 -0
  28. package/declarations/components/rating/state.d.ts.map +1 -1
  29. package/declarations/components/slider.d.ts +2 -0
  30. package/declarations/components/slider.d.ts.map +1 -1
  31. package/declarations/components/switch.d.ts +5 -1
  32. package/declarations/components/switch.d.ts.map +1 -1
  33. package/declarations/components/tabs.d.ts +30 -1
  34. package/declarations/components/tabs.d.ts.map +1 -1
  35. package/declarations/components/zoetrope/index.d.ts.map +1 -1
  36. package/declarations/index.d.ts +2 -1
  37. package/declarations/index.d.ts.map +1 -1
  38. package/dist/components/accordion.js +12 -4
  39. package/dist/components/accordion.js.map +1 -1
  40. package/dist/components/command-palette.js +370 -0
  41. package/dist/components/command-palette.js.map +1 -0
  42. package/dist/components/dialog.js +59 -3
  43. package/dist/components/dialog.js.map +1 -1
  44. package/dist/components/drawer.js +9 -2
  45. package/dist/components/drawer.js.map +1 -1
  46. package/dist/components/link.js +1 -1
  47. package/dist/components/link.js.map +1 -1
  48. package/dist/components/menu.js +5 -1
  49. package/dist/components/menu.js.map +1 -1
  50. package/dist/components/one-time-password.js +1 -1
  51. package/dist/components/rating.js +1 -1
  52. package/dist/components/slider.js +6 -1
  53. package/dist/components/switch.js +1 -1
  54. package/dist/components/switch.js.map +1 -1
  55. package/dist/components/tabs.js +18 -5
  56. package/dist/components/tabs.js.map +1 -1
  57. package/dist/components/zoetrope/styles.css +6 -1
  58. package/dist/components/zoetrope.js +1 -1
  59. package/dist/{index-gRO4Cvlf.js → index-B-yrfUe9.js} +10 -3
  60. package/dist/index-B-yrfUe9.js.map +1 -0
  61. package/dist/index.js +5 -4
  62. package/dist/index.js.map +1 -1
  63. package/dist/{otp-H5a4B4yu.js → otp-C8c1EIw6.js} +27 -15
  64. package/dist/otp-C8c1EIw6.js.map +1 -0
  65. package/dist/{rating-BrIiwDLw.js → rating-CKWhrr-L.js} +9 -5
  66. package/dist/rating-CKWhrr-L.js.map +1 -0
  67. package/package.json +2 -1
  68. package/src/components/accordion/content.gts +4 -2
  69. package/src/components/accordion/header.gts +8 -2
  70. package/src/components/accordion/item.gts +18 -2
  71. package/src/components/accordion/public.ts +12 -3
  72. package/src/components/accordion/trigger.gts +5 -3
  73. package/src/components/command-palette.gts +741 -0
  74. package/src/components/dialog.gts +86 -4
  75. package/src/components/drawer.gts +15 -3
  76. package/src/components/link.gts +1 -0
  77. package/src/components/menu.gts +7 -0
  78. package/src/components/one-time-password/input.gts +1 -1
  79. package/src/components/one-time-password/otp.gts +1 -1
  80. package/src/components/one-time-password/utils.ts +26 -13
  81. package/src/components/rating/range.gts +3 -0
  82. package/src/components/rating/rating.gts +7 -2
  83. package/src/components/rating/stars.gts +0 -1
  84. package/src/components/rating/state.gts +9 -0
  85. package/src/components/slider.gts +8 -0
  86. package/src/components/switch.gts +5 -2
  87. package/src/components/tabs.gts +35 -4
  88. package/src/components/zoetrope/index.gts +12 -3
  89. package/src/components/zoetrope/styles.css +6 -1
  90. package/src/index.ts +2 -1
  91. package/dist/index-gRO4Cvlf.js.map +0 -1
  92. package/dist/otp-H5a4B4yu.js.map +0 -1
  93. package/dist/rating-BrIiwDLw.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"drawer.js","sources":["../../src/components/drawer.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { assert } from \"@ember/debug\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\n\nimport { modifier as eModifier } from \"ember-modifier\";\n// temp\n// https://github.com/tracked-tools/tracked-toolbox/issues/38\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-expect-error\nimport { localCopy } from \"tracked-toolbox\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { ModifierLike, WithBoundArgs } from \"@glint/template\";\n\nconst DrawerElement: TOC<{\n Element: HTMLDialogElement;\n Args: {\n /**\n * @internal\n */\n open: boolean | undefined;\n /**\n * @internal\n */\n onClose: () => void;\n\n /**\n * @internal\n */\n register: ModifierLike<{ Element: HTMLDialogElement }>;\n };\n Blocks: { default: [] };\n}> = <template>\n <dialog ...attributes open={{@open}} {{on \"close\" @onClose}} {{@register}}>\n {{yield}}\n </dialog>\n</template>;\n\nexport interface Signature {\n Args: {\n /**\n * Optionally set the open state of the drawer\n * The state will still be managed internally,\n * so this does not need to be a maintained value, but whenever it changes,\n * the drawer element will reflect that change accordingly.\n */\n open?: boolean;\n /**\n * When the drawer is closed, this function will be called\n * and the drawer's `returnValue` will be passed.\n *\n * This can be used to determine which button was clicked to close the drawer\n *\n * Note though that this value is only populated when using\n * `<form method='dialog'>`\n */\n onClose?: (returnValue: string) => void;\n };\n Blocks: {\n default: [\n {\n /**\n * Represents the open state of the drawer element.\n */\n isOpen: boolean;\n\n /**\n * Closes the drawer element\n * Will throw an error if `Drawer` is not rendered.\n */\n close: () => void;\n\n /**\n * Opens the drawer element.\n * Will throw an error if `Drawer` is not rendered.\n */\n open: () => void;\n\n /**\n * This modifier should be applied to the button that opens the Drawer so that it can be re-focused when the drawer closes.\n *\n * Example:\n *\n * ```gjs\n * <template>\n * <Drawer as |d|>\n * <button {{d.focusOnClose}} {{on \"click\" d.open}}>Open</button>\n *\n * <d.Drawer>...</d.Drawer>\n * </Drawer>\n * </template>\n * ```\n */\n focusOnClose: ModifierLike<{ Element: HTMLElement }>;\n\n /**\n * This is the `<dialog>` element (with some defaults pre-wired).\n * This is required to be rendered.\n */\n Drawer: WithBoundArgs<typeof DrawerElement, \"onClose\" | \"register\" | \"open\">;\n },\n ];\n };\n}\n\nclass DrawerDialog extends Component<Signature> {\n <template>\n {{yield\n (hash\n isOpen=this.isOpen\n open=this.open\n close=this.close\n focusOnClose=this.refocus\n Drawer=(component DrawerElement open=@open onClose=this.handleClose register=this.register)\n )\n }}\n </template>\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n @localCopy(\"args.open\") declare _isOpen: boolean;\n\n get isOpen() {\n /**\n * Always fallback to false (closed)\n */\n return this._isOpen ?? false;\n }\n set isOpen(val: boolean) {\n this._isOpen = val;\n }\n\n #lastIsOpen = false;\n refocus = eModifier((element) => {\n assert(`focusOnClose is only valid on a HTMLElement`, element instanceof HTMLElement);\n\n if (!this.isOpen && this.#lastIsOpen) {\n element.focus();\n }\n\n this.#lastIsOpen = this.isOpen;\n });\n\n @tracked declare drawerElement: HTMLDialogElement | undefined;\n\n register = eModifier((element: HTMLDialogElement) => {\n /**\n * This is very sad.\n *\n * But we need the element to be 'root state'\n * so that when we read things like \"isOpen\",\n * when the drawer is finally rendered, all the\n * downstream properties render.\n *\n * This has to be an async / delayed a bit, so that\n * the tracking frame can exit, and we don't infinite loop\n */\n void (async () => {\n await Promise.resolve();\n\n this.drawerElement = element;\n })();\n });\n\n /**\n * Closes the drawer -- this will throw an error in development if the drawer element was not rendered\n */\n close = () => {\n assert(\n \"Cannot call `close` on <Drawer> without rendering the drawer element.\",\n this.drawerElement,\n );\n\n /**\n * If the element is already closed, don't run all this again\n */\n if (!this.drawerElement.hasAttribute(\"open\")) {\n return;\n }\n\n /**\n * removes the `open` attribute\n * handleClose will be called because the drawer has bound the `close` event.\n */\n this.drawerElement.close();\n };\n\n /**\n * @internal\n *\n * handles the <dialog> element's native close behavior.\n * listened to via addEventListener('close', ...);\n */\n handleClose = () => {\n assert(\n \"Cannot call `handleClose` on <Drawer> without rendering the drawer element. This is likely a bug in ember-primitives. Please open an issue <3\",\n this.drawerElement,\n );\n\n this.isOpen = false;\n this.args.onClose?.(this.drawerElement.returnValue);\n // the return value ends up staying... which is annoying\n this.drawerElement.returnValue = \"\";\n };\n\n /**\n * Opens the drawer -- this will throw an error in development if the drawer element was not rendered\n */\n open = () => {\n assert(\n \"Cannot call `open` on <Drawer> without rendering the drawer element.\",\n this.drawerElement,\n );\n\n /**\n * If the element is already open, don't run all this again\n */\n if (this.drawerElement.hasAttribute(\"open\")) {\n return;\n }\n\n /**\n * adds the `open` attribute\n */\n this.drawerElement.showModal();\n this.isOpen = true;\n };\n}\n\nexport const Drawer = DrawerDialog;\n\nexport default DrawerDialog;\n"],"names":["DrawerElement","setComponentTemplate","precompileTemplate","strictMode","scope","on","templateOnly","DrawerDialog","Component","hash","g","prototype","localCopy","i","isOpen","_isOpen","val","refocus","eModifier","element","assert","HTMLElement","focus","tracked","register","Promise","resolve","drawerElement","close","hasAttribute","handleClose","args","onClose","returnValue","open","showModal","Drawer"],"mappings":"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"}
1
+ {"version":3,"file":"drawer.js","sources":["../../src/components/drawer.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { assert } from \"@ember/debug\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\n\nimport { modifier as eModifier } from \"ember-modifier\";\n// temp\n// https://github.com/tracked-tools/tracked-toolbox/issues/38\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-expect-error\nimport { localCopy } from \"tracked-toolbox\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { ModifierLike, WithBoundArgs } from \"@glint/template\";\n\ninterface RegisterSignature {\n Element: HTMLDialogElement;\n Args: { Named: { open: boolean | undefined } };\n}\n\nconst DrawerElement: TOC<{\n Element: HTMLDialogElement;\n Args: {\n /**\n * @internal\n */\n open: boolean | undefined;\n /**\n * @internal\n */\n onClose: () => void;\n\n /**\n * @internal\n */\n register: ModifierLike<RegisterSignature>;\n };\n Blocks: { default: [] };\n}> = <template>\n <dialog ...attributes {{on \"close\" @onClose}} {{@register open=@open}}>\n {{yield}}\n </dialog>\n</template>;\n\nexport interface Signature {\n Args: {\n /**\n * Optionally set the open state of the drawer\n * The state will still be managed internally,\n * so this does not need to be a maintained value, but whenever it changes,\n * the drawer element will reflect that change accordingly.\n */\n open?: boolean;\n /**\n * When the drawer is closed, this function will be called\n * and the drawer's `returnValue` will be passed.\n *\n * This can be used to determine which button was clicked to close the drawer\n *\n * Note though that this value is only populated when using\n * `<form method='dialog'>`\n */\n onClose?: (returnValue: string) => void;\n };\n Blocks: {\n default: [\n {\n /**\n * Represents the open state of the drawer element.\n */\n isOpen: boolean;\n\n /**\n * Closes the drawer element\n * Will throw an error if `Drawer` is not rendered.\n */\n close: () => void;\n\n /**\n * Opens the drawer element.\n * Will throw an error if `Drawer` is not rendered.\n */\n open: () => void;\n\n /**\n * This modifier should be applied to the button that opens the Drawer so that it can be re-focused when the drawer closes.\n *\n * Example:\n *\n * ```gjs\n * <template>\n * <Drawer as |d|>\n * <button {{d.focusOnClose}} {{on \"click\" d.open}}>Open</button>\n *\n * <d.Drawer>...</d.Drawer>\n * </Drawer>\n * </template>\n * ```\n */\n focusOnClose: ModifierLike<{ Element: HTMLElement }>;\n\n /**\n * This is the `<dialog>` element (with some defaults pre-wired).\n * This is required to be rendered.\n */\n Drawer: WithBoundArgs<typeof DrawerElement, \"onClose\" | \"register\" | \"open\">;\n },\n ];\n };\n}\n\nclass DrawerDialog extends Component<Signature> {\n <template>\n {{yield\n (hash\n isOpen=this.isOpen\n open=this.open\n close=this.close\n focusOnClose=this.refocus\n Drawer=(component DrawerElement open=@open onClose=this.handleClose register=this.register)\n )\n }}\n </template>\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n @localCopy(\"args.open\") declare _isOpen: boolean;\n\n get isOpen() {\n /**\n * Always fallback to false (closed)\n */\n return this._isOpen ?? false;\n }\n set isOpen(val: boolean) {\n this._isOpen = val;\n }\n\n #lastIsOpen = false;\n refocus = eModifier((element) => {\n assert(`focusOnClose is only valid on a HTMLElement`, element instanceof HTMLElement);\n\n if (!this.isOpen && this.#lastIsOpen) {\n element.focus();\n }\n\n this.#lastIsOpen = this.isOpen;\n });\n\n @tracked declare drawerElement: HTMLDialogElement | undefined;\n\n register = eModifier<RegisterSignature>((element, _, { open }) => {\n /** The `open` attribute shows a non-modal drawer, only `showModal` is modal. */\n if (open && !element.open) {\n element.showModal();\n } else if (open === false && element.open) {\n element.close();\n }\n\n /**\n * This is very sad.\n *\n * But we need the element to be 'root state'\n * so that when we read things like \"isOpen\",\n * when the drawer is finally rendered, all the\n * downstream properties render.\n *\n * This has to be an async / delayed a bit, so that\n * the tracking frame can exit, and we don't infinite loop\n */\n void (async () => {\n await Promise.resolve();\n\n this.drawerElement = element;\n })();\n });\n\n /**\n * Closes the drawer -- this will throw an error in development if the drawer element was not rendered\n */\n close = () => {\n assert(\n \"Cannot call `close` on <Drawer> without rendering the drawer element.\",\n this.drawerElement,\n );\n\n /**\n * If the element is already closed, don't run all this again\n */\n if (!this.drawerElement.hasAttribute(\"open\")) {\n return;\n }\n\n /**\n * removes the `open` attribute\n * handleClose will be called because the drawer has bound the `close` event.\n */\n this.drawerElement.close();\n };\n\n /**\n * @internal\n *\n * handles the <dialog> element's native close behavior.\n * listened to via addEventListener('close', ...);\n */\n handleClose = () => {\n assert(\n \"Cannot call `handleClose` on <Drawer> without rendering the drawer element. This is likely a bug in ember-primitives. Please open an issue <3\",\n this.drawerElement,\n );\n\n this.isOpen = false;\n this.args.onClose?.(this.drawerElement.returnValue);\n // the return value ends up staying... which is annoying\n this.drawerElement.returnValue = \"\";\n };\n\n /**\n * Opens the drawer -- this will throw an error in development if the drawer element was not rendered\n */\n open = () => {\n assert(\n \"Cannot call `open` on <Drawer> without rendering the drawer element.\",\n this.drawerElement,\n );\n\n /**\n * If the element is already open, don't run all this again\n */\n if (this.drawerElement.hasAttribute(\"open\")) {\n return;\n }\n\n /**\n * adds the `open` attribute\n */\n this.drawerElement.showModal();\n this.isOpen = true;\n };\n}\n\nexport const Drawer = DrawerDialog;\n\nexport default DrawerDialog;\n"],"names":["DrawerElement","setComponentTemplate","precompileTemplate","strictMode","scope","on","templateOnly","DrawerDialog","Component","hash","g","prototype","localCopy","i","isOpen","_isOpen","val","refocus","eModifier","element","assert","HTMLElement","focus","tracked","register","_","open","showModal","close","Promise","resolve","drawerElement","hasAttribute","handleClose","args","onClose","returnValue","Drawer"],"mappings":"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"}
@@ -18,7 +18,7 @@ import templateOnly from '@ember/component/template-only';
18
18
  *
19
19
  * [mdn-a]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a
20
20
  */
21
- const Link = setComponentTemplate(precompileTemplate("{{#let (link @href includeActiveQueryParams=@includeActiveQueryParams activeOnSubPaths=@activeOnSubPaths) as |l|}}\n {{#if l.isExternal}}\n <ExternalLink href={{@href}} ...attributes>\n {{yield (hash isExternal=true isActive=false)}}\n </ExternalLink>\n {{else}}\n <a data-active={{l.isActive}} href={{if @href @href \"##missing##\"}} {{on \"click\" l.handleClick}} ...attributes>\n {{yield (hash isExternal=false isActive=l.isActive)}}\n </a>\n {{/if}}\n{{/let}}", {
21
+ const Link = setComponentTemplate(precompileTemplate("{{#let (link @href includeActiveQueryParams=@includeActiveQueryParams activeOnSubPaths=@activeOnSubPaths) as |l|}}\n {{#if l.isExternal}}\n <ExternalLink href={{@href}} ...attributes>\n {{yield (hash isExternal=true isActive=false)}}\n </ExternalLink>\n {{else}}\n <a data-active={{l.isActive}} aria-current={{if l.isActive (if @activeOnSubPaths \"true\" \"page\")}} href={{if @href @href \"##missing##\"}} {{on \"click\" l.handleClick}} ...attributes>\n {{yield (hash isExternal=false isActive=l.isActive)}}\n </a>\n {{/if}}\n{{/let}}", {
22
22
  strictMode: true,
23
23
  scope: () => ({
24
24
  link,
@@ -1 +1 @@
1
- {"version":3,"file":"link.js","sources":["../../src/components/link.gts"],"sourcesContent":["/**\n * TODO: make template-only component,\n * and use class-based modifier?\n *\n * This would require that modifiers could run pre-render\n */\nimport { hash } from '@ember/helper';\nimport { on } from '@ember/modifier';\n\nimport { link } from '../helpers/link.ts';\nimport { ExternalLink } from './external-link.gts';\n\nimport type { TOC } from '@ember/component/template-only';\n\nexport interface Signature {\n Element: HTMLAnchorElement;\n Args: {\n /**\n * the `href` string value to set on the anchor element.\n */\n href: string;\n /**\n * When calculating the \"active\" state of the link, you may decide\n * whether or not you want to _require_ that all query params be considered (true)\n * or specify individual query params, ignoring anything not specified.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Link } from 'ember-primitives';\n *\n * <template>\n * <Link @href=\"/\" @includeActiveQueryParams={{true}} as |a|>\n * ...\n * </Link>\n * </template>\n * ```\n *\n * the data-active state here will only be \"true\" on\n * - `/`\n * - `/?foo=2`\n * - `/?foo=&bar=`\n *\n */\n includeActiveQueryParams?: true | string[];\n /**\n * When calculating the \"active\" state of the link, you may decide\n * whether or not you want to consider sub paths to be active when\n * child routes/urls are active.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Link } from 'ember-primitives';\n *\n * <template>\n * <Link @href=\"/forum/1\" @activeOnSubPaths={{true}} as |a|>\n * ...\n * </Link>\n * </template>\n * ```\n *\n * the data-active state here will be \"true\" on\n * - `/forum/1`\n * - `/forum/1/posts`\n * - `/forum/1/posts/comments`\n * - `/forum/1/*etc*`\n *\n * if `@activeOnSubPaths` is set to false or left off\n * the data-active state here will only be \"true\" on\n * - `/forum/1`\n *\n */\n activeOnSubPaths?: true;\n };\n Blocks: {\n default: [\n {\n /**\n * Indicates if the passed `href` is pointing to an external site.\n * Useful if you want your links to have additional context for when\n * a user is about to leave your site.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Link } from 'ember-primitives';\n *\n * const MyLink = <template>\n * <Link @href={{@href}} as |a|>\n * {{yield}}\n * {{#if a.isExternal}}\n * ➚\n * {{/if}}\n * </Link>\n * </template>;\n *\n * <template>\n * <MyLink @href=\"https://developer.mozilla.org\">MDN</MyLink> &nbsp;&nbsp;\n * <MyLink @href=\"/\">Home</MyLink>\n * </template>\n * ```\n */\n isExternal: boolean;\n /**\n * Indicates if the passed `href` is *active*, or the user is on the same basepath.\n * This allows consumers to style their link if they wish or style their text.\n * The active state will also be present on a `data-active` attribute on the generated anchor tag.\n *\n *\n * For example\n * ```gjs\n * import { Link, service } from 'ember-primitives';\n *\n * const MyLink = <template>\n * <Link @href=\"...\"> as |a|>\n * <span class=\"{{if a.isActive 'underline'}}\">\n * {{yield}}\n * </span>\n * </Link>\n * </template>\n *\n * <template>\n * {{#let (service 'router') as |router|}}\n * <MyLink @href={{router.currentURL}}>Ths page</MyLink> &nbsp;&nbsp;\n * <MyLink @href=\"/\">Home</MyLink>\n * {{/let}}\n * </template>\n * ```\n *\n * By default, the query params are omitted from `isActive` calculation, but you may\n * configure the query params to be included if you wish\n * See: `@includeActiveQueryParams`\n *\n * By default, only the exact route/url is considered for the `isActive` calculation,\n * but you may configure sub routes/paths to also be considered active\n * See: `@activeOnSubPaths`\n *\n * Note that external links are never active.\n */\n isActive: boolean;\n },\n ];\n };\n}\n\n/**\n * A light wrapper around the [Anchor element][mdn-a], which will appropriately make your link an external link if the passed `@href` is not on the same domain.\n *\n *\n * [mdn-a]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a\n */\nexport const Link: TOC<Signature> = <template>\n {{#let (link @href includeActiveQueryParams=@includeActiveQueryParams activeOnSubPaths=@activeOnSubPaths) as |l|}}\n {{#if l.isExternal}}\n <ExternalLink href={{@href}} ...attributes>\n {{yield (hash isExternal=true isActive=false)}}\n </ExternalLink>\n {{else}}\n <a\n data-active={{l.isActive}}\n href={{if @href @href \"##missing##\"}}\n {{on \"click\" l.handleClick}}\n ...attributes\n >\n {{yield (hash isExternal=false isActive=l.isActive)}}\n </a>\n {{/if}}\n {{/let}}\n</template>;\n\nexport default Link;\n"],"names":["Link","setComponentTemplate","precompileTemplate","strictMode","scope","link","ExternalLink","hash","on","templateOnly"],"mappings":";;;;;;;;AAAA;;;;;AAKC;AA6ID;;;;;AAKC;MACYA,IAAU,GAAAC,oBAAA,CAAaC,kBAAA,CAAA,0eAAA,EAiBpC;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAC,IAAA;IAAAC,YAAA;IAAAC,IAAA;AAAAC,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA;;;;"}
1
+ {"version":3,"file":"link.js","sources":["../../src/components/link.gts"],"sourcesContent":["/**\n * TODO: make template-only component,\n * and use class-based modifier?\n *\n * This would require that modifiers could run pre-render\n */\nimport { hash } from '@ember/helper';\nimport { on } from '@ember/modifier';\n\nimport { link } from '../helpers/link.ts';\nimport { ExternalLink } from './external-link.gts';\n\nimport type { TOC } from '@ember/component/template-only';\n\nexport interface Signature {\n Element: HTMLAnchorElement;\n Args: {\n /**\n * the `href` string value to set on the anchor element.\n */\n href: string;\n /**\n * When calculating the \"active\" state of the link, you may decide\n * whether or not you want to _require_ that all query params be considered (true)\n * or specify individual query params, ignoring anything not specified.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Link } from 'ember-primitives';\n *\n * <template>\n * <Link @href=\"/\" @includeActiveQueryParams={{true}} as |a|>\n * ...\n * </Link>\n * </template>\n * ```\n *\n * the data-active state here will only be \"true\" on\n * - `/`\n * - `/?foo=2`\n * - `/?foo=&bar=`\n *\n */\n includeActiveQueryParams?: true | string[];\n /**\n * When calculating the \"active\" state of the link, you may decide\n * whether or not you want to consider sub paths to be active when\n * child routes/urls are active.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Link } from 'ember-primitives';\n *\n * <template>\n * <Link @href=\"/forum/1\" @activeOnSubPaths={{true}} as |a|>\n * ...\n * </Link>\n * </template>\n * ```\n *\n * the data-active state here will be \"true\" on\n * - `/forum/1`\n * - `/forum/1/posts`\n * - `/forum/1/posts/comments`\n * - `/forum/1/*etc*`\n *\n * if `@activeOnSubPaths` is set to false or left off\n * the data-active state here will only be \"true\" on\n * - `/forum/1`\n *\n */\n activeOnSubPaths?: true;\n };\n Blocks: {\n default: [\n {\n /**\n * Indicates if the passed `href` is pointing to an external site.\n * Useful if you want your links to have additional context for when\n * a user is about to leave your site.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Link } from 'ember-primitives';\n *\n * const MyLink = <template>\n * <Link @href={{@href}} as |a|>\n * {{yield}}\n * {{#if a.isExternal}}\n * ➚\n * {{/if}}\n * </Link>\n * </template>;\n *\n * <template>\n * <MyLink @href=\"https://developer.mozilla.org\">MDN</MyLink> &nbsp;&nbsp;\n * <MyLink @href=\"/\">Home</MyLink>\n * </template>\n * ```\n */\n isExternal: boolean;\n /**\n * Indicates if the passed `href` is *active*, or the user is on the same basepath.\n * This allows consumers to style their link if they wish or style their text.\n * The active state will also be present on a `data-active` attribute on the generated anchor tag.\n *\n *\n * For example\n * ```gjs\n * import { Link, service } from 'ember-primitives';\n *\n * const MyLink = <template>\n * <Link @href=\"...\"> as |a|>\n * <span class=\"{{if a.isActive 'underline'}}\">\n * {{yield}}\n * </span>\n * </Link>\n * </template>\n *\n * <template>\n * {{#let (service 'router') as |router|}}\n * <MyLink @href={{router.currentURL}}>Ths page</MyLink> &nbsp;&nbsp;\n * <MyLink @href=\"/\">Home</MyLink>\n * {{/let}}\n * </template>\n * ```\n *\n * By default, the query params are omitted from `isActive` calculation, but you may\n * configure the query params to be included if you wish\n * See: `@includeActiveQueryParams`\n *\n * By default, only the exact route/url is considered for the `isActive` calculation,\n * but you may configure sub routes/paths to also be considered active\n * See: `@activeOnSubPaths`\n *\n * Note that external links are never active.\n */\n isActive: boolean;\n },\n ];\n };\n}\n\n/**\n * A light wrapper around the [Anchor element][mdn-a], which will appropriately make your link an external link if the passed `@href` is not on the same domain.\n *\n *\n * [mdn-a]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a\n */\nexport const Link: TOC<Signature> = <template>\n {{#let (link @href includeActiveQueryParams=@includeActiveQueryParams activeOnSubPaths=@activeOnSubPaths) as |l|}}\n {{#if l.isExternal}}\n <ExternalLink href={{@href}} ...attributes>\n {{yield (hash isExternal=true isActive=false)}}\n </ExternalLink>\n {{else}}\n <a\n data-active={{l.isActive}}\n aria-current={{if l.isActive (if @activeOnSubPaths \"true\" \"page\")}}\n href={{if @href @href \"##missing##\"}}\n {{on \"click\" l.handleClick}}\n ...attributes\n >\n {{yield (hash isExternal=false isActive=l.isActive)}}\n </a>\n {{/if}}\n {{/let}}\n</template>;\n\nexport default Link;\n"],"names":["Link","setComponentTemplate","precompileTemplate","strictMode","scope","link","ExternalLink","hash","on","templateOnly"],"mappings":";;;;;;;;AAAA;;;;;AAKC;AA6ID;;;;;AAKC;MACYA,IAAU,GAAAC,oBAAA,CAAaC,kBAAA,CAAA,kjBAAA,EAkBpC;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAC,IAAA;IAAAC,YAAA;IAAAC,IAAA;AAAAC,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA;;;;"}
@@ -81,6 +81,7 @@ const installContent = modifier((element, _, {
81
81
  // listen for the escape key
82
82
  function onDocumentKeydown(e) {
83
83
  if (isOpen.current && e.key === "Escape") {
84
+ /** Otherwise the same key press also closes a `<dialog>` around the menu. */e.preventDefault();
84
85
  isOpen.current = false;
85
86
  }
86
87
  }
@@ -92,7 +93,7 @@ const installContent = modifier((element, _, {
92
93
  document.removeEventListener("keydown", onDocumentKeydown);
93
94
  };
94
95
  });
95
- const Content = setComponentTemplate(precompileTemplate("{{#if @isOpen.current}}\n <@PopoverContent id={{@contentId}} role=\"menu\" data-tabster={{TABSTER_CONFIG_CONTENT}} tabindex=\"0\" {{installContent isOpen=@isOpen triggerElement=@triggerElement}} ...attributes>\n {{yield (hash Item=(component Item toggle=@isOpen.toggle) LinkItem=(component LinkItem toggle=@isOpen.toggle) Separator=Separator)}}\n </@PopoverContent>\n{{/if}}", {
96
+ const Content = setComponentTemplate(precompileTemplate("{{#if @isOpen.current}}\n <@PopoverContent id={{@contentId}} role=\"menu\" aria-labelledby={{@triggerElement.current.id}} data-tabster={{TABSTER_CONFIG_CONTENT}} tabindex=\"0\" {{installContent isOpen=@isOpen triggerElement=@triggerElement}} ...attributes>\n {{yield (hash Item=(component Item toggle=@isOpen.toggle) LinkItem=(component LinkItem toggle=@isOpen.toggle) Separator=Separator)}}\n </@PopoverContent>\n{{/if}}", {
96
97
  strictMode: true,
97
98
  scope: () => ({
98
99
  TABSTER_CONFIG_CONTENT,
@@ -130,6 +131,9 @@ const trigger = modifier((element, _, {
130
131
  isOpen.toggle();
131
132
  };
132
133
  element.addEventListener("click", onTriggerClick);
134
+ if (!element.id) {
135
+ element.id = `${contentId}__trigger`;
136
+ }
133
137
  triggerElement.current = element;
134
138
  setReference(element);
135
139
  return () => {
@@ -1 +1 @@
1
- {"version":3,"file":"menu.js","sources":["../../src/components/menu.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { guidFor } from \"@ember/object/internals\";\n\nimport { modifier as eModifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\nimport { getTabster, getTabsterAttribute, MoverDirections, setTabsterAttribute } from \"tabster\";\n\nimport { Link, type Signature as LinkSignature } from \"./link.gts\";\nimport { Popover, type Signature as PopoverSignature } from \"./popover.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { WithBoundArgs } from \"@glint/template\";\n\ntype Cell<V> = ReturnType<typeof cell<V>>;\ntype LinkArgs = LinkSignature[\"Args\"];\ntype PopoverArgs = PopoverSignature[\"Args\"];\ntype PopoverBlockParams = PopoverSignature[\"Blocks\"][\"default\"][0];\n\nconst TABSTER_CONFIG_CONTENT = getTabsterAttribute(\n {\n mover: {\n direction: MoverDirections.Both,\n cyclic: true,\n },\n deloser: {},\n },\n true,\n);\n\nconst TABSTER_CONFIG_TRIGGER = {\n deloser: {},\n};\n\nexport interface Signature {\n Args: PopoverArgs;\n Blocks: {\n default: [\n {\n arrow: PopoverBlockParams[\"arrow\"];\n trigger: WithBoundArgs<\n typeof trigger,\n \"triggerElement\" | \"contentId\" | \"isOpen\" | \"setReference\"\n >;\n Trigger: WithBoundArgs<typeof Trigger, \"triggerModifier\">;\n Content: WithBoundArgs<\n typeof Content,\n \"triggerElement\" | \"contentId\" | \"isOpen\" | \"PopoverContent\"\n >;\n isOpen: boolean;\n },\n ];\n };\n}\n\nexport interface SeparatorSignature {\n Element: HTMLDivElement;\n Blocks: { default: [] };\n}\n\nconst Separator: TOC<SeparatorSignature> = <template>\n <div role=\"separator\" ...attributes>\n {{yield}}\n </div>\n</template>;\n\n/**\n * We focus items on `pointerMove` to achieve the following:\n *\n * - Mouse over an item (it focuses)\n * - Leave mouse where it is and use keyboard to focus a different item\n * - Wiggle mouse without it leaving previously focused item\n * - Previously focused item should re-focus\n *\n * If we used `mouseOver`/`mouseEnter` it would not re-focus when the mouse\n * wiggles. This is to match native menu implementation.\n */\nfunction focusOnHover(e: PointerEvent) {\n const item = e.currentTarget;\n\n if (item instanceof HTMLElement) {\n item?.focus();\n }\n}\n\ninterface PrivateItemSignature {\n Element: HTMLButtonElement;\n Args: { onSelect?: (event: Event) => void; toggle: () => void };\n Blocks: { default: [] };\n}\n\nexport interface ItemSignature {\n Element: PrivateItemSignature[\"Element\"];\n Args: Omit<PrivateItemSignature[\"Args\"], \"toggle\">;\n Blocks: PrivateItemSignature[\"Blocks\"];\n}\n\nconst Item: TOC<PrivateItemSignature> = <template>\n {{! @glint-expect-error }}\n {{#let (if @onSelect (modifier on \"click\" @onSelect)) as |maybeClick|}}\n <button\n type=\"button\"\n role=\"menuitem\"\n {{! @glint-expect-error }}\n {{maybeClick}}\n {{on \"click\" @toggle}}\n {{on \"pointermove\" focusOnHover}}\n ...attributes\n >\n {{yield}}\n </button>\n {{/let}}\n</template>;\n\ninterface LinkItemArgs extends LinkArgs {\n toggle: () => void;\n}\n\ninterface PrivateLinkItemSignature {\n Element: HTMLAnchorElement;\n Args: LinkItemArgs;\n Blocks: { default: [] };\n}\n\nexport interface LinkItemSignature {\n Element: PrivateLinkItemSignature[\"Element\"];\n Args: LinkArgs;\n Blocks: PrivateLinkItemSignature[\"Blocks\"];\n}\n\nconst LinkItem: TOC<PrivateLinkItemSignature> = <template>\n <Link\n role=\"menuitem\"\n @href={{@href}}\n @includeActiveQueryParams={{@includeActiveQueryParams}}\n @activeOnSubPaths={{@activeOnSubPaths}}\n {{on \"click\" @toggle}}\n {{on \"pointermove\" focusOnHover}}\n ...attributes\n >\n {{yield}}\n </Link>\n</template>;\n\nconst installContent = eModifier<{\n Element: HTMLElement;\n Args: {\n Named: {\n isOpen: Cell<boolean>;\n triggerElement: Cell<HTMLElement>;\n };\n };\n}>((element, _: [], { isOpen, triggerElement }) => {\n // Focus first focusable element when the popover opens.\n // The toggle event fires natively after showPopover() completes.\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/toggle_event\n function onToggle(e: ToggleEvent) {\n if (e.newState !== \"open\") return;\n\n const tabster = getTabster(window);\n const firstFocusable = tabster?.focusable.findFirst({\n container: element,\n });\n\n firstFocusable?.focus();\n }\n\n element.addEventListener(\"toggle\", onToggle as EventListener);\n\n // listen for \"outside\" clicks\n function onDocumentClick(e: MouseEvent) {\n if (\n isOpen.current &&\n e.target &&\n !element.contains(e.target as HTMLElement) &&\n !triggerElement.current?.contains(e.target as HTMLElement)\n ) {\n isOpen.current = false;\n }\n }\n\n // listen for the escape key\n function onDocumentKeydown(e: KeyboardEvent) {\n if (isOpen.current && e.key === \"Escape\") {\n isOpen.current = false;\n }\n }\n\n document.addEventListener(\"click\", onDocumentClick);\n document.addEventListener(\"keydown\", onDocumentKeydown);\n\n return () => {\n element.removeEventListener(\"toggle\", onToggle as EventListener);\n document.removeEventListener(\"click\", onDocumentClick);\n document.removeEventListener(\"keydown\", onDocumentKeydown);\n };\n});\n\ninterface PrivateContentSignature {\n Element: HTMLDivElement;\n Args: {\n triggerElement: Cell<HTMLElement>;\n contentId: string;\n isOpen: Cell<boolean>;\n PopoverContent: PopoverBlockParams[\"Content\"];\n };\n Blocks: {\n default: [\n {\n Item: WithBoundArgs<typeof Item, \"toggle\">;\n LinkItem: WithBoundArgs<typeof LinkItem, \"toggle\">;\n Separator: typeof Separator;\n },\n ];\n };\n}\n\nexport interface ContentSignature {\n Element: PrivateContentSignature[\"Element\"];\n Blocks: PrivateContentSignature[\"Blocks\"];\n}\n\nconst Content: TOC<PrivateContentSignature> = <template>\n {{#if @isOpen.current}}\n <@PopoverContent\n id={{@contentId}}\n role=\"menu\"\n data-tabster={{TABSTER_CONFIG_CONTENT}}\n tabindex=\"0\"\n {{installContent isOpen=@isOpen triggerElement=@triggerElement}}\n ...attributes\n >\n {{yield\n (hash\n Item=(component Item toggle=@isOpen.toggle)\n LinkItem=(component LinkItem toggle=@isOpen.toggle)\n Separator=Separator\n )\n }}\n </@PopoverContent>\n {{/if}}\n</template>;\n\ninterface PrivateTriggerModifierSignature {\n Element: HTMLElement;\n Args: {\n Named: {\n triggerElement: Cell<HTMLElement>;\n isOpen: Cell<boolean>;\n contentId: string;\n setReference: PopoverBlockParams[\"setReference\"];\n stopPropagation?: boolean;\n preventDefault?: boolean;\n };\n };\n}\n\nexport interface TriggerModifierSignature {\n Element: PrivateTriggerModifierSignature[\"Element\"];\n}\n\nconst trigger = eModifier<PrivateTriggerModifierSignature>(\n (\n element,\n _: [],\n { triggerElement, isOpen, contentId, setReference, stopPropagation, preventDefault },\n ) => {\n element.setAttribute(\"aria-haspopup\", \"menu\");\n\n if (isOpen.current) {\n element.setAttribute(\"aria-controls\", contentId);\n element.setAttribute(\"aria-expanded\", \"true\");\n } else {\n element.removeAttribute(\"aria-controls\");\n element.setAttribute(\"aria-expanded\", \"false\");\n }\n\n setTabsterAttribute(element, TABSTER_CONFIG_TRIGGER);\n\n const onTriggerClick = (event: MouseEvent) => {\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n if (preventDefault) {\n event.preventDefault();\n }\n\n isOpen.toggle();\n };\n\n element.addEventListener(\"click\", onTriggerClick);\n\n triggerElement.current = element;\n\n setReference(element);\n\n return () => {\n element.removeEventListener(\"click\", onTriggerClick);\n };\n },\n);\n\ninterface PrivateTriggerSignature {\n Element: HTMLButtonElement;\n Args: {\n triggerModifier: WithBoundArgs<\n typeof trigger,\n \"triggerElement\" | \"contentId\" | \"isOpen\" | \"setReference\"\n >;\n stopPropagation?: boolean;\n preventDefault?: boolean;\n };\n Blocks: { default: [] };\n}\n\nexport interface TriggerSignature {\n Element: PrivateTriggerSignature[\"Element\"];\n Blocks: PrivateTriggerSignature[\"Blocks\"];\n}\n\nconst Trigger: TOC<PrivateTriggerSignature> = <template>\n <button\n type=\"button\"\n {{@triggerModifier stopPropagation=@stopPropagation preventDefault=@preventDefault}}\n ...attributes\n >\n {{yield}}\n </button>\n</template>;\n\nconst IsOpen = () => cell<boolean>(false);\nconst TriggerElement = () => cell<HTMLElement>();\n\nexport class Menu extends Component<Signature> {\n contentId = guidFor(this);\n\n <template>\n {{#let (IsOpen) (TriggerElement) as |isOpen triggerEl|}}\n <Popover\n @flipOptions={{@flipOptions}}\n @middleware={{@middleware}}\n @offsetOptions={{@offsetOptions}}\n @placement={{@placement}}\n @shiftOptions={{@shiftOptions}}\n @strategy={{@strategy}}\n as |p|\n >\n {{#let\n (modifier\n trigger\n triggerElement=triggerEl\n isOpen=isOpen\n contentId=this.contentId\n setReference=p.setReference\n )\n as |triggerModifier|\n }}\n {{yield\n (hash\n trigger=triggerModifier\n Trigger=(component Trigger triggerModifier=triggerModifier)\n Content=(component\n Content\n PopoverContent=p.Content\n isOpen=isOpen\n triggerElement=triggerEl\n contentId=this.contentId\n )\n arrow=p.arrow\n isOpen=isOpen.current\n )\n }}\n {{/let}}\n </Popover>\n {{/let}}\n </template>\n}\n\nexport default Menu;\n"],"names":["TABSTER_CONFIG_CONTENT","getTabsterAttribute","mover","direction","MoverDirections","Both","cyclic","deloser","TABSTER_CONFIG_TRIGGER","Separator","setComponentTemplate","precompileTemplate","strictMode","templateOnly","focusOnHover","e","item","currentTarget","HTMLElement","focus","Item","scope","on","LinkItem","Link","installContent","eModifier","element","_","isOpen","triggerElement","onToggle","newState","tabster","getTabster","window","firstFocusable","focusable","findFirst","container","addEventListener","onDocumentClick","current","target","contains","onDocumentKeydown","key","document","removeEventListener","Content","hash","trigger","contentId","setReference","stopPropagation","preventDefault","setAttribute","removeAttribute","setTabsterAttribute","onTriggerClick","event","toggle","Trigger","IsOpen","cell","TriggerElement","Menu","Component","guidFor","Popover"],"mappings":"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"}
1
+ {"version":3,"file":"menu.js","sources":["../../src/components/menu.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { guidFor } from \"@ember/object/internals\";\n\nimport { modifier as eModifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\nimport { getTabster, getTabsterAttribute, MoverDirections, setTabsterAttribute } from \"tabster\";\n\nimport { Link, type Signature as LinkSignature } from \"./link.gts\";\nimport { Popover, type Signature as PopoverSignature } from \"./popover.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { WithBoundArgs } from \"@glint/template\";\n\ntype Cell<V> = ReturnType<typeof cell<V>>;\ntype LinkArgs = LinkSignature[\"Args\"];\ntype PopoverArgs = PopoverSignature[\"Args\"];\ntype PopoverBlockParams = PopoverSignature[\"Blocks\"][\"default\"][0];\n\nconst TABSTER_CONFIG_CONTENT = getTabsterAttribute(\n {\n mover: {\n direction: MoverDirections.Both,\n cyclic: true,\n },\n deloser: {},\n },\n true,\n);\n\nconst TABSTER_CONFIG_TRIGGER = {\n deloser: {},\n};\n\nexport interface Signature {\n Args: PopoverArgs;\n Blocks: {\n default: [\n {\n arrow: PopoverBlockParams[\"arrow\"];\n trigger: WithBoundArgs<\n typeof trigger,\n \"triggerElement\" | \"contentId\" | \"isOpen\" | \"setReference\"\n >;\n Trigger: WithBoundArgs<typeof Trigger, \"triggerModifier\">;\n Content: WithBoundArgs<\n typeof Content,\n \"triggerElement\" | \"contentId\" | \"isOpen\" | \"PopoverContent\"\n >;\n isOpen: boolean;\n },\n ];\n };\n}\n\nexport interface SeparatorSignature {\n Element: HTMLDivElement;\n Blocks: { default: [] };\n}\n\nconst Separator: TOC<SeparatorSignature> = <template>\n <div role=\"separator\" ...attributes>\n {{yield}}\n </div>\n</template>;\n\n/**\n * We focus items on `pointerMove` to achieve the following:\n *\n * - Mouse over an item (it focuses)\n * - Leave mouse where it is and use keyboard to focus a different item\n * - Wiggle mouse without it leaving previously focused item\n * - Previously focused item should re-focus\n *\n * If we used `mouseOver`/`mouseEnter` it would not re-focus when the mouse\n * wiggles. This is to match native menu implementation.\n */\nfunction focusOnHover(e: PointerEvent) {\n const item = e.currentTarget;\n\n if (item instanceof HTMLElement) {\n item?.focus();\n }\n}\n\ninterface PrivateItemSignature {\n Element: HTMLButtonElement;\n Args: { onSelect?: (event: Event) => void; toggle: () => void };\n Blocks: { default: [] };\n}\n\nexport interface ItemSignature {\n Element: PrivateItemSignature[\"Element\"];\n Args: Omit<PrivateItemSignature[\"Args\"], \"toggle\">;\n Blocks: PrivateItemSignature[\"Blocks\"];\n}\n\nconst Item: TOC<PrivateItemSignature> = <template>\n {{! @glint-expect-error }}\n {{#let (if @onSelect (modifier on \"click\" @onSelect)) as |maybeClick|}}\n <button\n type=\"button\"\n role=\"menuitem\"\n {{! @glint-expect-error }}\n {{maybeClick}}\n {{on \"click\" @toggle}}\n {{on \"pointermove\" focusOnHover}}\n ...attributes\n >\n {{yield}}\n </button>\n {{/let}}\n</template>;\n\ninterface LinkItemArgs extends LinkArgs {\n toggle: () => void;\n}\n\ninterface PrivateLinkItemSignature {\n Element: HTMLAnchorElement;\n Args: LinkItemArgs;\n Blocks: { default: [] };\n}\n\nexport interface LinkItemSignature {\n Element: PrivateLinkItemSignature[\"Element\"];\n Args: LinkArgs;\n Blocks: PrivateLinkItemSignature[\"Blocks\"];\n}\n\nconst LinkItem: TOC<PrivateLinkItemSignature> = <template>\n <Link\n role=\"menuitem\"\n @href={{@href}}\n @includeActiveQueryParams={{@includeActiveQueryParams}}\n @activeOnSubPaths={{@activeOnSubPaths}}\n {{on \"click\" @toggle}}\n {{on \"pointermove\" focusOnHover}}\n ...attributes\n >\n {{yield}}\n </Link>\n</template>;\n\nconst installContent = eModifier<{\n Element: HTMLElement;\n Args: {\n Named: {\n isOpen: Cell<boolean>;\n triggerElement: Cell<HTMLElement>;\n };\n };\n}>((element, _: [], { isOpen, triggerElement }) => {\n // Focus first focusable element when the popover opens.\n // The toggle event fires natively after showPopover() completes.\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/toggle_event\n function onToggle(e: ToggleEvent) {\n if (e.newState !== \"open\") return;\n\n const tabster = getTabster(window);\n const firstFocusable = tabster?.focusable.findFirst({\n container: element,\n });\n\n firstFocusable?.focus();\n }\n\n element.addEventListener(\"toggle\", onToggle as EventListener);\n\n // listen for \"outside\" clicks\n function onDocumentClick(e: MouseEvent) {\n if (\n isOpen.current &&\n e.target &&\n !element.contains(e.target as HTMLElement) &&\n !triggerElement.current?.contains(e.target as HTMLElement)\n ) {\n isOpen.current = false;\n }\n }\n\n // listen for the escape key\n function onDocumentKeydown(e: KeyboardEvent) {\n if (isOpen.current && e.key === \"Escape\") {\n /** Otherwise the same key press also closes a `<dialog>` around the menu. */\n e.preventDefault();\n isOpen.current = false;\n }\n }\n\n document.addEventListener(\"click\", onDocumentClick);\n document.addEventListener(\"keydown\", onDocumentKeydown);\n\n return () => {\n element.removeEventListener(\"toggle\", onToggle as EventListener);\n document.removeEventListener(\"click\", onDocumentClick);\n document.removeEventListener(\"keydown\", onDocumentKeydown);\n };\n});\n\ninterface PrivateContentSignature {\n Element: HTMLDivElement;\n Args: {\n triggerElement: Cell<HTMLElement>;\n contentId: string;\n isOpen: Cell<boolean>;\n PopoverContent: PopoverBlockParams[\"Content\"];\n };\n Blocks: {\n default: [\n {\n Item: WithBoundArgs<typeof Item, \"toggle\">;\n LinkItem: WithBoundArgs<typeof LinkItem, \"toggle\">;\n Separator: typeof Separator;\n },\n ];\n };\n}\n\nexport interface ContentSignature {\n Element: PrivateContentSignature[\"Element\"];\n Blocks: PrivateContentSignature[\"Blocks\"];\n}\n\nconst Content: TOC<PrivateContentSignature> = <template>\n {{#if @isOpen.current}}\n <@PopoverContent\n id={{@contentId}}\n role=\"menu\"\n aria-labelledby={{@triggerElement.current.id}}\n data-tabster={{TABSTER_CONFIG_CONTENT}}\n tabindex=\"0\"\n {{installContent isOpen=@isOpen triggerElement=@triggerElement}}\n ...attributes\n >\n {{yield\n (hash\n Item=(component Item toggle=@isOpen.toggle)\n LinkItem=(component LinkItem toggle=@isOpen.toggle)\n Separator=Separator\n )\n }}\n </@PopoverContent>\n {{/if}}\n</template>;\n\ninterface PrivateTriggerModifierSignature {\n Element: HTMLElement;\n Args: {\n Named: {\n triggerElement: Cell<HTMLElement>;\n isOpen: Cell<boolean>;\n contentId: string;\n setReference: PopoverBlockParams[\"setReference\"];\n stopPropagation?: boolean;\n preventDefault?: boolean;\n };\n };\n}\n\nexport interface TriggerModifierSignature {\n Element: PrivateTriggerModifierSignature[\"Element\"];\n}\n\nconst trigger = eModifier<PrivateTriggerModifierSignature>(\n (\n element,\n _: [],\n { triggerElement, isOpen, contentId, setReference, stopPropagation, preventDefault },\n ) => {\n element.setAttribute(\"aria-haspopup\", \"menu\");\n\n if (isOpen.current) {\n element.setAttribute(\"aria-controls\", contentId);\n element.setAttribute(\"aria-expanded\", \"true\");\n } else {\n element.removeAttribute(\"aria-controls\");\n element.setAttribute(\"aria-expanded\", \"false\");\n }\n\n setTabsterAttribute(element, TABSTER_CONFIG_TRIGGER);\n\n const onTriggerClick = (event: MouseEvent) => {\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n if (preventDefault) {\n event.preventDefault();\n }\n\n isOpen.toggle();\n };\n\n element.addEventListener(\"click\", onTriggerClick);\n\n if (!element.id) {\n element.id = `${contentId}__trigger`;\n }\n\n triggerElement.current = element;\n\n setReference(element);\n\n return () => {\n element.removeEventListener(\"click\", onTriggerClick);\n };\n },\n);\n\ninterface PrivateTriggerSignature {\n Element: HTMLButtonElement;\n Args: {\n triggerModifier: WithBoundArgs<\n typeof trigger,\n \"triggerElement\" | \"contentId\" | \"isOpen\" | \"setReference\"\n >;\n stopPropagation?: boolean;\n preventDefault?: boolean;\n };\n Blocks: { default: [] };\n}\n\nexport interface TriggerSignature {\n Element: PrivateTriggerSignature[\"Element\"];\n Blocks: PrivateTriggerSignature[\"Blocks\"];\n}\n\nconst Trigger: TOC<PrivateTriggerSignature> = <template>\n <button\n type=\"button\"\n {{@triggerModifier stopPropagation=@stopPropagation preventDefault=@preventDefault}}\n ...attributes\n >\n {{yield}}\n </button>\n</template>;\n\nconst IsOpen = () => cell<boolean>(false);\nconst TriggerElement = () => cell<HTMLElement>();\n\nexport class Menu extends Component<Signature> {\n contentId = guidFor(this);\n\n <template>\n {{#let (IsOpen) (TriggerElement) as |isOpen triggerEl|}}\n <Popover\n @flipOptions={{@flipOptions}}\n @middleware={{@middleware}}\n @offsetOptions={{@offsetOptions}}\n @placement={{@placement}}\n @shiftOptions={{@shiftOptions}}\n @strategy={{@strategy}}\n as |p|\n >\n {{#let\n (modifier\n trigger\n triggerElement=triggerEl\n isOpen=isOpen\n contentId=this.contentId\n setReference=p.setReference\n )\n as |triggerModifier|\n }}\n {{yield\n (hash\n trigger=triggerModifier\n Trigger=(component Trigger triggerModifier=triggerModifier)\n Content=(component\n Content\n PopoverContent=p.Content\n isOpen=isOpen\n triggerElement=triggerEl\n contentId=this.contentId\n )\n arrow=p.arrow\n isOpen=isOpen.current\n )\n }}\n {{/let}}\n </Popover>\n {{/let}}\n </template>\n}\n\nexport default Menu;\n"],"names":["TABSTER_CONFIG_CONTENT","getTabsterAttribute","mover","direction","MoverDirections","Both","cyclic","deloser","TABSTER_CONFIG_TRIGGER","Separator","setComponentTemplate","precompileTemplate","strictMode","templateOnly","focusOnHover","e","item","currentTarget","HTMLElement","focus","Item","scope","on","LinkItem","Link","installContent","eModifier","element","_","isOpen","triggerElement","onToggle","newState","tabster","getTabster","window","firstFocusable","focusable","findFirst","container","addEventListener","onDocumentClick","current","target","contains","onDocumentKeydown","key","preventDefault","document","removeEventListener","Content","hash","trigger","contentId","setReference","stopPropagation","setAttribute","removeAttribute","setTabsterAttribute","onTriggerClick","event","toggle","id","Trigger","IsOpen","cell","TriggerElement","Menu","Component","guidFor","Popover"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export { O as OTP, a as OTPInput } from '../otp-H5a4B4yu.js';
1
+ export { O as OTP, a as OTPInput } from '../otp-C8c1EIw6.js';
2
2
  //# sourceMappingURL=one-time-password.js.map
@@ -1,2 +1,2 @@
1
- export { R as Rating } from '../rating-BrIiwDLw.js';
1
+ export { R as Rating } from '../rating-CKWhrr-L.js';
2
2
  //# sourceMappingURL=rating.js.map
@@ -23,6 +23,11 @@ class ThumbComponent extends Component {
23
23
  // When using tick values, the `input` needs the internal index.
24
24
  return this.args.thumb?.inputValue ?? this.args.value ?? this.args.store.internalMin;
25
25
  }
26
+ /** With tick values the input holds an index, which means nothing when read aloud. */
27
+ get valueText() {
28
+ const tick = this.args.store.tickValues?.[this.value];
29
+ return tick === undefined ? undefined : String(tick);
30
+ }
26
31
  get isActive() {
27
32
  return this.args.store.activeThumbIndex === this.index;
28
33
  }
@@ -57,7 +62,7 @@ class ThumbComponent extends Component {
57
62
  this.args.store.handleThumbActivate(this.index);
58
63
  };
59
64
  static {
60
- setComponentTemplate(precompileTemplate("<input ...attributes class=\"ember-primitives__slider__thumb-input\" type=\"range\" min={{@store.internalMin}} max={{@store.internalMax}} step={{@store.internalStep}} value={{this.value}} disabled={{@store.disabled}} data-active={{if this.isActive \"\"}} {{on \"gotpointercapture\" this.onGotPointerCapture}} {{on \"pointerup\" this.onPointerUp}} {{on \"focus\" this.onFocus}} {{on \"input\" this.onInput}} {{on \"change\" this.onChange}} />\n<span class=\"ember-primitives__slider__thumb\" style={{this.positionStyle}} data-active={{if this.isActive \"\"}} data-disabled={{if @store.disabled \"\"}} aria-hidden=\"true\">{{yield}}</span>", {
65
+ setComponentTemplate(precompileTemplate("<input aria-valuetext={{this.valueText}} ...attributes class=\"ember-primitives__slider__thumb-input\" type=\"range\" min={{@store.internalMin}} max={{@store.internalMax}} step={{@store.internalStep}} value={{this.value}} disabled={{@store.disabled}} data-active={{if this.isActive \"\"}} {{on \"gotpointercapture\" this.onGotPointerCapture}} {{on \"pointerup\" this.onPointerUp}} {{on \"focus\" this.onFocus}} {{on \"input\" this.onInput}} {{on \"change\" this.onChange}} />\n<span class=\"ember-primitives__slider__thumb\" style={{this.positionStyle}} data-active={{if this.isActive \"\"}} data-disabled={{if @store.disabled \"\"}} aria-hidden=\"true\">{{yield}}</span>", {
61
66
  strictMode: true,
62
67
  scope: () => ({
63
68
  on
@@ -24,7 +24,7 @@ class Checkbox extends Component {
24
24
  }
25
25
  };
26
26
  static {
27
- setComponentTemplate(precompileTemplate("<input id={{@id}} type=\"checkbox\" role=\"switch\" checked={{@checked.current}} aria-checked={{@checked.current}} data-state={{if @checked.current \"on\" \"off\"}} {{on \"click\" this.handleClick}} ...attributes />", {
27
+ setComponentTemplate(precompileTemplate("<input id={{@id}} type=\"checkbox\" role=\"switch\" checked={{@checked.current}} data-state={{if @checked.current \"on\" \"off\"}} {{on \"click\" this.handleClick}} ...attributes />", {
28
28
  strictMode: true,
29
29
  scope: () => ({
30
30
  on
@@ -1 +1 @@
1
- {"version":3,"file":"switch.js","sources":["../../src/components/-private/typed-elements.gts","../../src/components/switch.gts"],"sourcesContent":["import type { TOC } from \"@ember/component/template-only\";\n\nexport const Div: TOC<{ Element: HTMLDivElement; Blocks: { default: [] } }> = <template>\n <div ...attributes>{{yield}}</div>\n</template>;\n\nexport const Label: TOC<{\n Element: HTMLLabelElement;\n Args: { for: string };\n Blocks: { default: [] };\n}> = <template>\n <label for={{@for}} ...attributes>{{yield}}</label>\n</template>;\n","import Component from \"@glimmer/component\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\n\nimport { cell } from \"ember-resources\";\n\nimport { uniqueId } from \"../utils.ts\";\nimport { Label } from \"./-private/typed-elements.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { WithBoundArgs } from \"@glint/template\";\n\nexport interface Signature {\n Element: HTMLInputElement;\n Args: {\n /**\n * The initial checked value of the Switch.\n * This value is reactive, so if the value that\n * `@checked` is set to updates, the state of the Switch will also update.\n */\n checked?: boolean;\n /**\n * Callback when the Switch state is toggled\n */\n onChange?: (checked: boolean, event: Event) => void;\n };\n Blocks: {\n default?: [\n {\n /**\n * The current state of the Switch.\n *\n * ```gjs\n * import { Switch } from 'ember-primitives/components/switch';\n *\n * <template>\n * <Switch as |s|>\n * {{s.isChecked}}\n * </Switch>\n * </template>\n * ```\n */\n isChecked: boolean;\n /**\n * The Switch Element.\n * It has a pre-wired `id` so that the relevant Label is\n * appropriately associated via the `for` property of the Label.\n *\n * ```gjs\n * import { Switch } from 'ember-primitives/components/switch';\n *\n * <template>\n * <Switch as |s|>\n * <s.Control />\n * </Switch>\n * </template>\n * ```\n */\n Control: WithBoundArgs<typeof Checkbox, \"checked\" | \"id\" | \"onChange\">;\n /**\n * The Switch element requires a label, and this label already has\n * the association to the Control by setting the `for` attribute to the `id` of the Control\n *\n * ```gjs\n * import { Switch } from 'ember-primitive/components/switchs';\n *\n * <template>\n * <Switch as |s|>\n * <s.Label />\n * </Switch>\n * </template>\n * ```\n */\n Label: WithBoundArgs<typeof Label, \"for\">;\n },\n ];\n };\n}\n\ninterface ControlSignature {\n Element: HTMLInputElement;\n Args: {\n id: string;\n checked?: ReturnType<typeof cell<boolean>>;\n onChange?: (checked: boolean, event: Event) => void;\n };\n}\n\nclass Checkbox extends Component<ControlSignature> {\n handleClick = (event: Event) => {\n const newChecked = (event.target as HTMLInputElement).checked;\n\n if (this.args.onChange) {\n this.args.onChange(newChecked, event);\n } else {\n this.args.checked?.toggle();\n }\n };\n\n <template>\n <input\n id={{@id}}\n type=\"checkbox\"\n role=\"switch\"\n checked={{@checked.current}}\n aria-checked={{@checked.current}}\n data-state={{if @checked.current \"on\" \"off\"}}\n {{on \"click\" this.handleClick}}\n ...attributes\n />\n </template>\n}\n\nfunction defaultFalse(value: unknown) {\n return value ?? false;\n}\n\n/**\n * @public\n */\nexport const Switch: TOC<Signature> = <template>\n <div ...attributes data-prim-switch>\n {{#let (uniqueId) as |id|}}\n {{#let (cell (defaultFalse @checked)) as |checked|}}\n {{! @glint-nocheck }}\n {{yield\n (hash\n isChecked=checked.current\n Control=(component Checkbox checked=checked id=id onChange=@onChange)\n Label=(component Label for=id)\n )\n }}\n {{/let}}\n {{/let}}\n </div>\n</template>;\n\nexport default Switch;\n"],"names":["precompileTemplate","strictMode","templateOnly","Label","Checkbox","Component","handleClick","event","newChecked","target","checked","args","onChange","toggle","setComponentTemplate","scope","on","defaultFalse","value","Switch","uniqueId","cell","hash"],"mappings":";;;;;;;;;qBAE8EA,kBAAA,CAAA,oCAAA,EAE9E;EAAAC,UAAA,EAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA;AAEH,MAAMC,6BAIRH,kBAAA,CAAA,qDAAA,EAEL;EAAAC,UAAA,EAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA,CAAA;;AC4EV,MAAME,iBAAiBC,SAAA,CAAU;EAC/BC,WAAA,GAAeC,KAAO,IAAA;AACpB,IAAA,MAAMC,aAAcD,KAAA,CAAME,MAAM,CAAsBC,OAAO;AAE7D,IAAA,IAAI,IAAI,CAACC,IAAI,CAACC,QAAQ,EAAE;MACtB,IAAI,CAACD,IAAI,CAACC,QAAQ,CAACJ,UAAA,EAAYD,KAAA,CAAA;AACjC,IAAA,CAAA,MAAO;AACL,MAAA,IAAI,CAACI,IAAI,CAACD,OAAO,EAAEG,MAAA,EAAA;AACrB,IAAA;EACF,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAd,kBAAA,CAAA,yNAAA,EAWA;MAAAC,UAAA,EAAA,IAAA;AAAAc,MAAAA,KAAA,EAAAA,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASC,YAAAA,CAAaC,KAAc,EAAA;EAClC,OAAOA,KAAA,IAAS,KAAA;AAClB;AAEA;;AAEC;MACYC,MAAY,GAAAL,oBAAA,CAAad,kBAAA,CAAA,yVAAA,EAetC;EAAAC,UAAA,EAAA,IAAA;AAAAc,EAAAA,KAAA,EAAAA,OAAA;IAAAK,QAAA;IAAAC,IAAA;IAAAJ,YAAA;IAAAK,IAAA;IAAAlB,QAAA;AAAAD,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAD,YAAA,EAAA;;;;"}
1
+ {"version":3,"file":"switch.js","sources":["../../src/components/-private/typed-elements.gts","../../src/components/switch.gts"],"sourcesContent":["import type { TOC } from \"@ember/component/template-only\";\n\nexport const Div: TOC<{ Element: HTMLDivElement; Blocks: { default: [] } }> = <template>\n <div ...attributes>{{yield}}</div>\n</template>;\n\nexport const Label: TOC<{\n Element: HTMLLabelElement;\n Args: { for: string };\n Blocks: { default: [] };\n}> = <template>\n <label for={{@for}} ...attributes>{{yield}}</label>\n</template>;\n","import Component from \"@glimmer/component\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\n\nimport { cell } from \"ember-resources\";\n\nimport { uniqueId } from \"../utils.ts\";\nimport { Label } from \"./-private/typed-elements.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { WithBoundArgs } from \"@glint/template\";\n\nexport interface Signature {\n /**\n * The wrapping element.\n * An accessible name belongs on `Control`, or comes from `Label`.\n */\n Element: HTMLDivElement;\n Args: {\n /**\n * The initial checked value of the Switch.\n * This value is reactive, so if the value that\n * `@checked` is set to updates, the state of the Switch will also update.\n */\n checked?: boolean;\n /**\n * Callback when the Switch state is toggled\n */\n onChange?: (checked: boolean, event: Event) => void;\n };\n Blocks: {\n default?: [\n {\n /**\n * The current state of the Switch.\n *\n * ```gjs\n * import { Switch } from 'ember-primitives/components/switch';\n *\n * <template>\n * <Switch as |s|>\n * {{s.isChecked}}\n * </Switch>\n * </template>\n * ```\n */\n isChecked: boolean;\n /**\n * The Switch Element.\n * It has a pre-wired `id` so that the relevant Label is\n * appropriately associated via the `for` property of the Label.\n *\n * ```gjs\n * import { Switch } from 'ember-primitives/components/switch';\n *\n * <template>\n * <Switch as |s|>\n * <s.Control />\n * </Switch>\n * </template>\n * ```\n */\n Control: WithBoundArgs<typeof Checkbox, \"checked\" | \"id\" | \"onChange\">;\n /**\n * The Switch element requires a label, and this label already has\n * the association to the Control by setting the `for` attribute to the `id` of the Control\n *\n * ```gjs\n * import { Switch } from 'ember-primitive/components/switchs';\n *\n * <template>\n * <Switch as |s|>\n * <s.Label />\n * </Switch>\n * </template>\n * ```\n */\n Label: WithBoundArgs<typeof Label, \"for\">;\n },\n ];\n };\n}\n\ninterface ControlSignature {\n Element: HTMLInputElement;\n Args: {\n id: string;\n checked?: ReturnType<typeof cell<boolean>>;\n onChange?: (checked: boolean, event: Event) => void;\n };\n}\n\nclass Checkbox extends Component<ControlSignature> {\n handleClick = (event: Event) => {\n const newChecked = (event.target as HTMLInputElement).checked;\n\n if (this.args.onChange) {\n this.args.onChange(newChecked, event);\n } else {\n this.args.checked?.toggle();\n }\n };\n\n <template>\n <input\n id={{@id}}\n type=\"checkbox\"\n role=\"switch\"\n checked={{@checked.current}}\n data-state={{if @checked.current \"on\" \"off\"}}\n {{on \"click\" this.handleClick}}\n ...attributes\n />\n </template>\n}\n\nfunction defaultFalse(value: unknown) {\n return value ?? false;\n}\n\n/**\n * @public\n */\nexport const Switch: TOC<Signature> = <template>\n <div ...attributes data-prim-switch>\n {{#let (uniqueId) as |id|}}\n {{#let (cell (defaultFalse @checked)) as |checked|}}\n {{! @glint-nocheck }}\n {{yield\n (hash\n isChecked=checked.current\n Control=(component Checkbox checked=checked id=id onChange=@onChange)\n Label=(component Label for=id)\n )\n }}\n {{/let}}\n {{/let}}\n </div>\n</template>;\n\nexport default Switch;\n"],"names":["precompileTemplate","strictMode","templateOnly","Label","Checkbox","Component","handleClick","event","newChecked","target","checked","args","onChange","toggle","setComponentTemplate","scope","on","defaultFalse","value","Switch","uniqueId","cell","hash"],"mappings":";;;;;;;;;qBAE8EA,kBAAA,CAAA,oCAAA,EAE9E;EAAAC,UAAA,EAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA;AAEH,MAAMC,6BAIRH,kBAAA,CAAA,qDAAA,EAEL;EAAAC,UAAA,EAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA,CAAA;;ACgFV,MAAME,iBAAiBC,SAAA,CAAU;EAC/BC,WAAA,GAAeC,KAAO,IAAA;AACpB,IAAA,MAAMC,aAAcD,KAAA,CAAME,MAAM,CAAsBC,OAAO;AAE7D,IAAA,IAAI,IAAI,CAACC,IAAI,CAACC,QAAQ,EAAE;MACtB,IAAI,CAACD,IAAI,CAACC,QAAQ,CAACJ,UAAA,EAAYD,KAAA,CAAA;AACjC,IAAA,CAAA,MAAO;AACL,MAAA,IAAI,CAACI,IAAI,CAACD,OAAO,EAAEG,MAAA,EAAA;AACrB,IAAA;EACF,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAd,kBAAA,CAAA,uLAAA,EAUA;MAAAC,UAAA,EAAA,IAAA;AAAAc,MAAAA,KAAA,EAAAA,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASC,YAAAA,CAAaC,KAAc,EAAA;EAClC,OAAOA,KAAA,IAAS,KAAA;AAClB;AAEA;;AAEC;MACYC,MAAY,GAAAL,oBAAA,CAAad,kBAAA,CAAA,yVAAA,EAetC;EAAAC,UAAA,EAAA,IAAA;AAAAc,EAAAA,KAAA,EAAAA,OAAA;IAAAK,QAAA;IAAAC,IAAA;IAAAJ,YAAA;IAAAK,IAAA;IAAAlB,QAAA;AAAAD,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAD,YAAA,EAAA;;;;"}
@@ -26,21 +26,34 @@ const TABSTER_CONFIG = getTabsterAttribute({
26
26
  mover: {
27
27
  direction: MoverDirections.Both,
28
28
  cyclic: true,
29
+ hasDefault: true,
29
30
  memorizeCurrent: true
30
31
  },
31
32
  deloser: {}
32
33
  }, true);
33
- const TabLink = setComponentTemplate(precompileTemplate("<a href=\"##missing##\" ...attributes role=\"tab\" aria-controls={{@panelId}} id={{@id}}>\n {{yield}}\n</a>", {
34
- strictMode: true
34
+ const TABSTER_SELECTED_TAB = getTabsterAttribute({
35
+ focusable: {
36
+ isDefault: true
37
+ }
38
+ }, true);
39
+ function tabsterFor(isSelected) {
40
+ return isSelected ? TABSTER_SELECTED_TAB : undefined;
41
+ }
42
+ const TabLink = setComponentTemplate(precompileTemplate("<a href=\"##missing##\" ...attributes role=\"tab\" aria-controls={{@panelId}} aria-selected={{String (@state.isActive @id @value)}} id={{@id}}>\n {{yield}}\n</a>", {
43
+ strictMode: true,
44
+ scope: () => ({
45
+ String
46
+ })
35
47
  }), templateOnly());
36
- const TabButton = setComponentTemplate(precompileTemplate("<button ...attributes role=\"tab\" type=\"button\" aria-controls={{@panelId}} aria-selected={{String (@state.isActive @id @value)}} id={{@id}} {{on \"click\" @handleClick}} {{!-- The Types for modifier are wrong --}} {{!-- @glint-expect-error--}} {{(if @state.isAutomatic (modifier on \"focus\" @handleClick))}}>\n {{yield}}\n</button>", {
48
+ const TabButton = setComponentTemplate(precompileTemplate("<button ...attributes role=\"tab\" type=\"button\" aria-controls={{@panelId}} aria-selected={{String (@state.isActive @id @value)}} data-tabster={{tabsterFor (@state.isActive @id @value)}} id={{@id}} {{on \"click\" @handleClick}} {{!-- The Types for modifier are wrong --}} {{!-- @glint-expect-error--}} {{(if @state.isAutomatic (modifier on \"focus\" @handleClick))}}>\n {{yield}}\n</button>", {
37
49
  strictMode: true,
38
50
  scope: () => ({
39
51
  String,
52
+ tabsterFor,
40
53
  on
41
54
  })
42
55
  }), templateOnly());
43
- const TabContent = setComponentTemplate(precompileTemplate("<Portal @to=\"#{{@state.tabpanelId}}\" @append={{true}}>\n {{#if (@state.isActive @tabId)}}\n <div ...attributes role=\"tabpanel\" aria-labelledby={{@tabId}} id={{@id}}>\n {{yield}}\n </div>\n {{/if}}\n</Portal>", {
56
+ const TabContent = setComponentTemplate(precompileTemplate("<Portal @to=\"#{{@state.tabpanelId}}\" @append={{true}}>\n {{#if (@state.isActive @tabId)}}\n <div tabindex=\"0\" ...attributes role=\"tabpanel\" aria-labelledby={{@tabId}} id={{@id}}>\n {{yield}}\n </div>\n {{/if}}\n</Portal>", {
44
57
  strictMode: true,
45
58
  scope: () => ({
46
59
  Portal
@@ -66,7 +79,7 @@ class TabContainer extends Component {
66
79
  return this.args.label ?? this.tabId;
67
80
  }
68
81
  static {
69
- setComponentTemplate(precompileTemplate("{{#if @label}}\n <TabButton @state={{@state}} @id={{this.tabId}} @value={{@value}} @panelId={{this.panelId}} @handleClick={{fn @state.handleChange this.tabId @value}}>\n {{#if (isString @label)}}\n {{@label}}\n {{else}}\n <@label />\n {{/if}}\n </TabButton>\n\n <TabContent @state={{@state}} @id={{this.panelId}} @tabId={{this.tabId}}>\n {{#if @content}}\n {{#if (isString @content)}}\n {{@content}}\n {{else}}\n <@content />\n {{/if}}\n {{else}}\n {{yield}}\n {{/if}}\n </TabContent>\n{{else}}\n {{yield (makeTab (component TabButton state=@state value=@value id=this.tabId panelId=this.panelId handleClick=(fn @state.handleChange this.tabId @value)) (component TabLink state=@state id=this.tabId panelId=this.panelId)) (component TabContent state=@state id=this.panelId tabId=this.tabId)}}\n{{/if}}", {
82
+ setComponentTemplate(precompileTemplate("{{#if @label}}\n <TabButton @state={{@state}} @id={{this.tabId}} @value={{@value}} @panelId={{this.panelId}} @handleClick={{fn @state.handleChange this.tabId @value}}>\n {{#if (isString @label)}}\n {{@label}}\n {{else}}\n <@label />\n {{/if}}\n </TabButton>\n\n <TabContent @state={{@state}} @id={{this.panelId}} @tabId={{this.tabId}}>\n {{#if @content}}\n {{#if (isString @content)}}\n {{@content}}\n {{else}}\n <@content />\n {{/if}}\n {{else}}\n {{yield}}\n {{/if}}\n </TabContent>\n{{else}}\n {{yield (makeTab (component TabButton state=@state value=@value id=this.tabId panelId=this.panelId handleClick=(fn @state.handleChange this.tabId @value)) (component TabLink state=@state value=@value id=this.tabId panelId=this.panelId)) (component TabContent state=@state id=this.panelId tabId=this.tabId)}}\n{{/if}}", {
70
83
  strictMode: true,
71
84
  scope: () => ({
72
85
  TabButton,
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.js","sources":["../../src/components/tabs.gts"],"sourcesContent":["/**\n * References:\n * - https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tablist_role\n * - https://www.w3.org/WAI/ARIA/apg/patterns/tabs/\n *\n *\n * Keyboard behaviors (optionally) provided by tabster\n */\n\nimport Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { isDestroyed, isDestroying } from \"@ember/destroyable\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { next } from \"@ember/runloop\";\n\nimport { getTabsterAttribute, MoverDirections } from \"tabster\";\n\nimport { uniqueId } from \"../utils.ts\";\nimport Portal from \"./portal.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type Owner from \"@ember/owner\";\nimport type { ComponentLike, WithBoundArgs } from \"@glint/template\";\n\nconst UNSET = Symbol.for(\"ember-primitives:tabs:unset\");\n\nconst TABSTER_CONFIG = getTabsterAttribute(\n {\n mover: {\n direction: MoverDirections.Both,\n cyclic: true,\n memorizeCurrent: true,\n },\n deloser: {},\n },\n true,\n);\n\nconst TabLink: TOC<{\n Element: HTMLAnchorElement;\n Args: {\n /**\n * @internal\n * for linking of aria\n */\n id: string;\n /**\n * @internal\n * for linking of aria\n */\n panelId: string;\n };\n Blocks: { default: [] };\n}> = <template>\n <a href=\"##missing##\" ...attributes role=\"tab\" aria-controls={{@panelId}} id={{@id}}>\n {{yield}}\n </a>\n</template>;\n\nexport type ButtonType = ComponentLike<ButtonSignature>;\nexport interface ButtonSignature {\n Element: HTMLButtonElement;\n Blocks: {\n default: [];\n };\n}\n\nconst TabButton: TOC<{\n Element: HTMLButtonElement;\n Args: {\n /**\n * @internal\n * for linking of aria\n */\n id: string;\n /**\n * @internal\n * for linking of aria\n */\n panelId: string;\n\n /**\n * @internal\n * for managing state\n */\n handleClick: () => void;\n\n /**\n * @internal\n * for managing state\n */\n value: string | undefined;\n\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: {\n default: [];\n };\n}> = <template>\n <button\n ...attributes\n role=\"tab\"\n type=\"button\"\n aria-controls={{@panelId}}\n aria-selected={{String (@state.isActive @id @value)}}\n id={{@id}}\n {{on \"click\" @handleClick}}\n {{! The Types for modifier are wrong }}\n {{! @glint-expect-error}}\n {{(if @state.isAutomatic (modifier on \"focus\" @handleClick))}}\n >\n {{yield}}\n </button>\n</template>;\n\nexport type ContentType = ComponentLike<ContentSignature>;\nexport interface ContentSignature {\n /**\n * the [role=tabpanel] element\n */\n Element: HTMLDivElement;\n Blocks: {\n default: [];\n };\n}\n\nconst TabContent: TOC<{\n Element: HTMLDivElement;\n Args: {\n /**\n * @internal\n * for linking of aria\n */\n id: string;\n /**\n * @internal\n * for linking of aria\n */\n tabId: string;\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: {\n default: [];\n };\n}> = <template>\n <Portal @to=\"#{{@state.tabpanelId}}\" @append={{true}}>\n {{#if (@state.isActive @tabId)}}\n <div ...attributes role=\"tabpanel\" aria-labelledby={{@tabId}} id={{@id}}>\n {{yield}}\n </div>\n {{/if}}\n </Portal>\n</template>;\n\nfunction isString(x: unknown): x is string {\n return typeof x === \"string\";\n}\n\nfunction makeTab(tabButton: any, tabLink: any): any {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n tabButton.Link = tabLink;\n\n return tabButton;\n}\n\nexport type ContainerType = ComponentLike<ContainerSignature>;\nexport type ContainerSignature =\n | {\n Blocks: {\n default: [];\n };\n }\n | {\n Args: {\n label: string | ComponentLike;\n content: string | ComponentLike;\n };\n }\n | {\n Args: {\n label: string | ComponentLike;\n };\n Blocks: {\n /**\n * The content for the tab\n */\n default: [];\n };\n };\n\nclass TabContainer extends Component<{\n Args: {\n /**\n * @internal\n */\n state: TabState;\n\n /**\n * When opting for a \"controlled component\",\n * the value will be needed to make sense of the selected tab.\n *\n * The default value used for communication within the Tabs component (and eventually emitted via the @onChange argument) is a unique random id.\n * So while that could still be used for controlling the tabs component, it may be more easy to grok with user-managed values.\n */\n value?: string;\n\n /**\n * optional user-passable label\n */\n label?: string | ComponentLike;\n\n /**\n * optional user-passable content.\n */\n content?: string | ComponentLike;\n };\n Blocks: {\n default: [\n Label: WithBoundArgs<typeof TabButton, \"state\" | \"id\" | \"panelId\" | \"handleClick\" | \"value\">,\n Content: WithBoundArgs<typeof TabContent, \"state\" | \"id\" | \"tabId\">,\n ];\n };\n}> {\n id = `ember-primitives__tab-${uniqueId()}`;\n\n get tabId() {\n return `${this.id}__tab`;\n }\n\n get panelId() {\n return `${this.id}__panel`;\n }\n\n get label() {\n return this.args.label ?? this.tabId;\n }\n\n <template>\n {{#if @label}}\n <TabButton\n @state={{@state}}\n @id={{this.tabId}}\n @value={{@value}}\n @panelId={{this.panelId}}\n @handleClick={{fn @state.handleChange this.tabId @value}}\n >\n {{#if (isString @label)}}\n {{@label}}\n {{else}}\n <@label />\n {{/if}}\n </TabButton>\n\n <TabContent @state={{@state}} @id={{this.panelId}} @tabId={{this.tabId}}>\n {{#if @content}}\n {{#if (isString @content)}}\n {{@content}}\n {{else}}\n <@content />\n {{/if}}\n {{else}}\n {{yield}}\n {{/if}}\n </TabContent>\n {{else}}\n {{yield\n (makeTab\n (component\n TabButton\n state=@state\n value=@value\n id=this.tabId\n panelId=this.panelId\n handleClick=(fn @state.handleChange this.tabId @value)\n )\n (component TabLink state=@state id=this.tabId panelId=this.panelId)\n )\n (component TabContent state=@state id=this.panelId tabId=this.tabId)\n }}\n {{/if}}\n </template>\n}\n\nconst Label: TOC<{\n /**\n * The label wiring (id, aria, etc) are handled for you.\n * If you'd like to use a heading element (h3, etc), place that in the block content\n * when invoking this Label component.\n */\n Element: null;\n Args: {\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: { default: [] };\n}> = <template>\n <Portal @to=\"#{{@state.labelId}}\">\n {{yield}}\n </Portal>\n</template>;\n\nexport interface Signature {\n /**\n * The wrapping element for the overall Tabs component.\n * This should be used for styling the layout of the tabs.\n */\n Element: HTMLDivElement;\n Args: {\n /**\n * Sets the active tab.\n * If not passed, the first tab will be selected\n */\n activeTab?: string;\n\n /**\n * Optional label for the overall TabList\n */\n label?: string | ComponentLike;\n\n /**\n * When the tab changes, this function will be called.\n * The function receives both the newly selected tab as well as the previous tab.\n *\n * However, if the tabs are not configured with names, these values will be null.\n */\n onChange?: (selectedTab: string, previousTab: string | null) => void;\n\n /**\n * When activationMode is set to \"automatic\", tabs are activated when receiving focus. When set to \"manual\", tabs are activated when clicked (or when \"enter\" is pressed via the keyboard).\n */\n activationMode?: \"automatic\" | \"manual\";\n };\n Blocks: {\n default: [\n Tab: WithBoundArgs<typeof TabContainer, \"state\"> & {\n Label: WithBoundArgs<typeof Label, \"state\">;\n },\n ];\n };\n}\n\n/**\n * We're doing old skool hax with this, so we don't need to care about what the types think, really\n */\nfunction makeAPI(tabContainer: any, labelComponent: any): any {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment\n tabContainer.Label = labelComponent;\n\n return tabContainer;\n}\n\nimport { buildWaiter } from \"@ember/test-waiters\";\n\nconst stateWaiter = buildWaiter(\"ember-primitives:tabs\");\n\n/**\n * State bucket passed around to all the sub-components.\n *\n * Sort of a \"Context\", but with a bit of prop-drilling (which is more efficient than dom-context)\n */\nclass TabState {\n declare args: {\n activeTab?: string;\n activationMode?: \"automatic\" | \"manual\";\n onChange?: (selected: string, previous: string | null) => void;\n };\n\n @tracked _active: string | null = null;\n\n @tracked _label: string | undefined;\n\n #first: string | null = null;\n id: string;\n labelId: string;\n tabpanelId: string;\n #token: unknown;\n\n constructor(args: { activeTab?: string; onChange?: () => void }) {\n this.args = args;\n\n this.id = `ember-primitives-${uniqueId()}`;\n this.labelId = `${this.id}__label`;\n this.tabpanelId = `${this.id}__tabpanel`;\n }\n\n get activationMode() {\n return this.args.activationMode ?? \"automatic\";\n }\n\n get isAutomatic() {\n return this.activationMode === \"automatic\";\n }\n\n /**\n * This function relies on the fact that during rendering,\n * the first component to be rendered will be first,\n * and it will be the one to set the secret first value,\n * which means all other tabs will not be first.\n *\n */\n isActive = (tabId: string, tabValue: undefined | string) => {\n /**\n * When users pass the @value to a tab, we use that for managing\n * the \"active state\" instead of the DOM ID.\n *\n * NOTE: DOM IDs must be unique across the whole document, but @value\n * does not need to be unqiue.\n * `@value` *should* be unique for the Tabs component though\n */\n const isSelected = (x: string) => {\n if (tabValue) return x === tabValue;\n\n return x === tabId;\n };\n\n if (this.active === UNSET) {\n if (this.#first) return isSelected(this.#first);\n\n this.#first = tabValue ?? tabId;\n this.#token = stateWaiter.beginAsync();\n\n // eslint-disable-next-line ember/no-runloop\n next(() => {\n if (!this.#token) return;\n stateWaiter.endAsync(this.#token);\n if (this._active) return;\n if (isDestroyed(this) || isDestroying(this)) return;\n\n this._label = tabValue ?? tabId;\n });\n\n return true;\n }\n\n return isSelected(this.active);\n };\n\n get active() {\n return this._active ?? this.args.activeTab ?? UNSET;\n }\n\n get activeLabel() {\n /**\n * This is only needed during the first set\n * because we prioritize rendering first, and then updating metadata later\n * (next render)\n *\n * NOTE: this does not mean that the a11y tree is updated later.\n * it is correct on initial render\n */\n if (this._label) {\n return this._label;\n }\n\n if (this.active === UNSET) {\n return \"Pending\";\n }\n\n return this.active;\n }\n\n handleChange = (tabId: string, tabValue: string | undefined) => {\n const previous = this.active;\n const next = tabValue ?? tabId;\n\n // No change, no need to be noisy\n if (next === previous) return;\n\n this._active = this._label = next;\n\n this.args.onChange?.(next, previous === UNSET ? null : previous);\n };\n}\n\nexport class Tabs extends Component<Signature> {\n state: TabState;\n\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n constructor(owner: Owner, args: {}) {\n super(owner, args);\n\n this.state = new TabState(args);\n }\n\n <template>\n <div class=\"ember-primitives__tabs\" ...attributes data-active={{this.state.activeLabel}}>\n {{! This element will be portaled in to and replaced if tabs.Label is invoked }}\n <div class=\"ember-primitives__tabs__label\" id={{this.state.labelId}}>\n {{#if (isString @label)}}\n {{@label}}\n {{else}}\n <@label />\n {{/if}}\n </div>\n <div\n class=\"ember-primitives__tabs__tablist\"\n role=\"tablist\"\n aria-labelledby={{this.state.labelId}}\n data-tabster={{TABSTER_CONFIG}}\n >\n {{yield\n (makeAPI (component TabContainer state=this.state) (component Label state=this.state))\n }}\n </div>\n {{!\n Tab's contents are portaled in to this element\n }}\n <div class=\"ember-primitives__tabs__tabpanel\" id={{this.state.tabpanelId}}></div>\n </div>\n </template>\n}\n"],"names":["UNSET","Symbol","for","TABSTER_CONFIG","getTabsterAttribute","mover","direction","MoverDirections","Both","cyclic","memorizeCurrent","deloser","TabLink","setComponentTemplate","precompileTemplate","strictMode","templateOnly","TabButton","scope","String","on","TabContent","Portal","isString","x","makeTab","tabButton","tabLink","Link","TabContainer","Component","id","uniqueId","tabId","panelId","label","args","fn","Label","makeAPI","tabContainer","labelComponent","stateWaiter","buildWaiter","TabState","g","prototype","tracked","i","labelId","tabpanelId","constructor","activationMode","isAutomatic","isActive","tabValue","isSelected","active","beginAsync","next","endAsync","_active","isDestroyed","isDestroying","_label","activeTab","activeLabel","handleChange","previous","onChange","Tabs","state","owner"],"mappings":"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"}
1
+ {"version":3,"file":"tabs.js","sources":["../../src/components/tabs.gts"],"sourcesContent":["/**\n * References:\n * - https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tablist_role\n * - https://www.w3.org/WAI/ARIA/apg/patterns/tabs/\n *\n *\n * Keyboard behaviors (optionally) provided by tabster\n */\n\nimport Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { isDestroyed, isDestroying } from \"@ember/destroyable\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { next } from \"@ember/runloop\";\n\nimport { getTabsterAttribute, MoverDirections } from \"tabster\";\n\nimport { uniqueId } from \"../utils.ts\";\nimport Portal from \"./portal.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type Owner from \"@ember/owner\";\nimport type { ComponentLike, WithBoundArgs } from \"@glint/template\";\n\nconst UNSET = Symbol.for(\"ember-primitives:tabs:unset\");\n\nconst TABSTER_CONFIG = getTabsterAttribute(\n {\n mover: {\n direction: MoverDirections.Both,\n cyclic: true,\n hasDefault: true,\n memorizeCurrent: true,\n },\n deloser: {},\n },\n true,\n);\n\nconst TABSTER_SELECTED_TAB = getTabsterAttribute({ focusable: { isDefault: true } }, true);\n\nfunction tabsterFor(isSelected: boolean) {\n return isSelected ? TABSTER_SELECTED_TAB : undefined;\n}\n\nconst TabLink: TOC<{\n Element: HTMLAnchorElement;\n Args: {\n /**\n * @internal\n * for linking of aria\n */\n id: string;\n /**\n * @internal\n * for linking of aria\n */\n panelId: string;\n\n /**\n * @internal\n * for managing state\n */\n value: string | undefined;\n\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: { default: [] };\n}> = <template>\n <a\n href=\"##missing##\"\n ...attributes\n role=\"tab\"\n aria-controls={{@panelId}}\n aria-selected={{String (@state.isActive @id @value)}}\n id={{@id}}\n >\n {{yield}}\n </a>\n</template>;\n\nexport type ButtonType = ComponentLike<ButtonSignature>;\nexport interface ButtonSignature {\n Element: HTMLButtonElement;\n Blocks: {\n default: [];\n };\n}\n\nconst TabButton: TOC<{\n Element: HTMLButtonElement;\n Args: {\n /**\n * @internal\n * for linking of aria\n */\n id: string;\n /**\n * @internal\n * for linking of aria\n */\n panelId: string;\n\n /**\n * @internal\n * for managing state\n */\n handleClick: () => void;\n\n /**\n * @internal\n * for managing state\n */\n value: string | undefined;\n\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: {\n default: [];\n };\n}> = <template>\n <button\n ...attributes\n role=\"tab\"\n type=\"button\"\n aria-controls={{@panelId}}\n aria-selected={{String (@state.isActive @id @value)}}\n data-tabster={{tabsterFor (@state.isActive @id @value)}}\n id={{@id}}\n {{on \"click\" @handleClick}}\n {{! The Types for modifier are wrong }}\n {{! @glint-expect-error}}\n {{(if @state.isAutomatic (modifier on \"focus\" @handleClick))}}\n >\n {{yield}}\n </button>\n</template>;\n\nexport type ContentType = ComponentLike<ContentSignature>;\nexport interface ContentSignature {\n /**\n * the [role=tabpanel] element\n */\n Element: HTMLDivElement;\n Blocks: {\n default: [];\n };\n}\n\nconst TabContent: TOC<{\n Element: HTMLDivElement;\n Args: {\n /**\n * @internal\n * for linking of aria\n */\n id: string;\n /**\n * @internal\n * for linking of aria\n */\n tabId: string;\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: {\n default: [];\n };\n}> = <template>\n <Portal @to=\"#{{@state.tabpanelId}}\" @append={{true}}>\n {{#if (@state.isActive @tabId)}}\n <div tabindex=\"0\" ...attributes role=\"tabpanel\" aria-labelledby={{@tabId}} id={{@id}}>\n {{yield}}\n </div>\n {{/if}}\n </Portal>\n</template>;\n\nfunction isString(x: unknown): x is string {\n return typeof x === \"string\";\n}\n\nfunction makeTab(tabButton: any, tabLink: any): any {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n tabButton.Link = tabLink;\n\n return tabButton;\n}\n\nexport type ContainerType = ComponentLike<ContainerSignature>;\nexport type ContainerSignature =\n | {\n Blocks: {\n default: [];\n };\n }\n | {\n Args: {\n label: string | ComponentLike;\n content: string | ComponentLike;\n };\n }\n | {\n Args: {\n label: string | ComponentLike;\n };\n Blocks: {\n /**\n * The content for the tab\n */\n default: [];\n };\n };\n\nclass TabContainer extends Component<{\n Args: {\n /**\n * @internal\n */\n state: TabState;\n\n /**\n * When opting for a \"controlled component\",\n * the value will be needed to make sense of the selected tab.\n *\n * The default value used for communication within the Tabs component (and eventually emitted via the @onChange argument) is a unique random id.\n * So while that could still be used for controlling the tabs component, it may be more easy to grok with user-managed values.\n */\n value?: string;\n\n /**\n * optional user-passable label\n */\n label?: string | ComponentLike;\n\n /**\n * optional user-passable content.\n */\n content?: string | ComponentLike;\n };\n Blocks: {\n default: [\n Label: WithBoundArgs<\n typeof TabButton,\n \"state\" | \"id\" | \"panelId\" | \"handleClick\" | \"value\"\n > & {\n Link: WithBoundArgs<typeof TabLink, \"state\" | \"value\" | \"id\" | \"panelId\">;\n },\n Content: WithBoundArgs<typeof TabContent, \"state\" | \"id\" | \"tabId\">,\n ];\n };\n}> {\n id = `ember-primitives__tab-${uniqueId()}`;\n\n get tabId() {\n return `${this.id}__tab`;\n }\n\n get panelId() {\n return `${this.id}__panel`;\n }\n\n get label() {\n return this.args.label ?? this.tabId;\n }\n\n <template>\n {{#if @label}}\n <TabButton\n @state={{@state}}\n @id={{this.tabId}}\n @value={{@value}}\n @panelId={{this.panelId}}\n @handleClick={{fn @state.handleChange this.tabId @value}}\n >\n {{#if (isString @label)}}\n {{@label}}\n {{else}}\n <@label />\n {{/if}}\n </TabButton>\n\n <TabContent @state={{@state}} @id={{this.panelId}} @tabId={{this.tabId}}>\n {{#if @content}}\n {{#if (isString @content)}}\n {{@content}}\n {{else}}\n <@content />\n {{/if}}\n {{else}}\n {{yield}}\n {{/if}}\n </TabContent>\n {{else}}\n {{yield\n (makeTab\n (component\n TabButton\n state=@state\n value=@value\n id=this.tabId\n panelId=this.panelId\n handleClick=(fn @state.handleChange this.tabId @value)\n )\n (component TabLink state=@state value=@value id=this.tabId panelId=this.panelId)\n )\n (component TabContent state=@state id=this.panelId tabId=this.tabId)\n }}\n {{/if}}\n </template>\n}\n\nconst Label: TOC<{\n /**\n * The label wiring (id, aria, etc) are handled for you.\n * If you'd like to use a heading element (h3, etc), place that in the block content\n * when invoking this Label component.\n */\n Element: null;\n Args: {\n /**\n * @internal\n */\n state: TabState;\n };\n Blocks: { default: [] };\n}> = <template>\n <Portal @to=\"#{{@state.labelId}}\">\n {{yield}}\n </Portal>\n</template>;\n\nexport interface Signature {\n /**\n * The wrapping element for the overall Tabs component.\n * This should be used for styling the layout of the tabs.\n */\n Element: HTMLDivElement;\n Args: {\n /**\n * Sets the active tab.\n * If not passed, the first tab will be selected\n */\n activeTab?: string;\n\n /**\n * Optional label for the overall TabList\n */\n label?: string | ComponentLike;\n\n /**\n * When the tab changes, this function will be called.\n * The function receives both the newly selected tab as well as the previous tab.\n *\n * However, if the tabs are not configured with names, these values will be null.\n */\n onChange?: (selectedTab: string, previousTab: string | null) => void;\n\n /**\n * When activationMode is set to \"automatic\", tabs are activated when receiving focus. When set to \"manual\", tabs are activated when clicked (or when \"enter\" is pressed via the keyboard).\n */\n activationMode?: \"automatic\" | \"manual\";\n };\n Blocks: {\n default: [\n Tab: WithBoundArgs<typeof TabContainer, \"state\"> & {\n Label: WithBoundArgs<typeof Label, \"state\">;\n },\n ];\n };\n}\n\n/**\n * We're doing old skool hax with this, so we don't need to care about what the types think, really\n */\nfunction makeAPI(tabContainer: any, labelComponent: any): any {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment\n tabContainer.Label = labelComponent;\n\n return tabContainer;\n}\n\nimport { buildWaiter } from \"@ember/test-waiters\";\n\nconst stateWaiter = buildWaiter(\"ember-primitives:tabs\");\n\n/**\n * State bucket passed around to all the sub-components.\n *\n * Sort of a \"Context\", but with a bit of prop-drilling (which is more efficient than dom-context)\n */\nclass TabState {\n declare args: {\n activeTab?: string;\n activationMode?: \"automatic\" | \"manual\";\n onChange?: (selected: string, previous: string | null) => void;\n };\n\n @tracked _active: string | null = null;\n\n @tracked _label: string | undefined;\n\n #first: string | null = null;\n id: string;\n labelId: string;\n tabpanelId: string;\n #token: unknown;\n\n constructor(args: { activeTab?: string; onChange?: () => void }) {\n this.args = args;\n\n this.id = `ember-primitives-${uniqueId()}`;\n this.labelId = `${this.id}__label`;\n this.tabpanelId = `${this.id}__tabpanel`;\n }\n\n get activationMode() {\n return this.args.activationMode ?? \"automatic\";\n }\n\n get isAutomatic() {\n return this.activationMode === \"automatic\";\n }\n\n /**\n * This function relies on the fact that during rendering,\n * the first component to be rendered will be first,\n * and it will be the one to set the secret first value,\n * which means all other tabs will not be first.\n *\n */\n isActive = (tabId: string, tabValue: undefined | string) => {\n /**\n * When users pass the @value to a tab, we use that for managing\n * the \"active state\" instead of the DOM ID.\n *\n * NOTE: DOM IDs must be unique across the whole document, but @value\n * does not need to be unqiue.\n * `@value` *should* be unique for the Tabs component though\n */\n const isSelected = (x: string) => {\n if (tabValue) return x === tabValue;\n\n return x === tabId;\n };\n\n if (this.active === UNSET) {\n if (this.#first) return isSelected(this.#first);\n\n this.#first = tabValue ?? tabId;\n this.#token = stateWaiter.beginAsync();\n\n // eslint-disable-next-line ember/no-runloop\n next(() => {\n if (!this.#token) return;\n stateWaiter.endAsync(this.#token);\n if (this._active) return;\n if (isDestroyed(this) || isDestroying(this)) return;\n\n this._label = tabValue ?? tabId;\n });\n\n return true;\n }\n\n return isSelected(this.active);\n };\n\n get active() {\n return this._active ?? this.args.activeTab ?? UNSET;\n }\n\n get activeLabel() {\n /**\n * This is only needed during the first set\n * because we prioritize rendering first, and then updating metadata later\n * (next render)\n *\n * NOTE: this does not mean that the a11y tree is updated later.\n * it is correct on initial render\n */\n if (this._label) {\n return this._label;\n }\n\n if (this.active === UNSET) {\n return \"Pending\";\n }\n\n return this.active;\n }\n\n handleChange = (tabId: string, tabValue: string | undefined) => {\n const previous = this.active;\n const next = tabValue ?? tabId;\n\n // No change, no need to be noisy\n if (next === previous) return;\n\n this._active = this._label = next;\n\n this.args.onChange?.(next, previous === UNSET ? null : previous);\n };\n}\n\nexport class Tabs extends Component<Signature> {\n state: TabState;\n\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n constructor(owner: Owner, args: {}) {\n super(owner, args);\n\n this.state = new TabState(args);\n }\n\n <template>\n <div class=\"ember-primitives__tabs\" ...attributes data-active={{this.state.activeLabel}}>\n {{! This element will be portaled in to and replaced if tabs.Label is invoked }}\n <div class=\"ember-primitives__tabs__label\" id={{this.state.labelId}}>\n {{#if (isString @label)}}\n {{@label}}\n {{else}}\n <@label />\n {{/if}}\n </div>\n <div\n class=\"ember-primitives__tabs__tablist\"\n role=\"tablist\"\n aria-labelledby={{this.state.labelId}}\n data-tabster={{TABSTER_CONFIG}}\n >\n {{yield\n (makeAPI (component TabContainer state=this.state) (component Label state=this.state))\n }}\n </div>\n {{!\n Tab's contents are portaled in to this element\n }}\n <div class=\"ember-primitives__tabs__tabpanel\" id={{this.state.tabpanelId}}></div>\n </div>\n </template>\n}\n"],"names":["UNSET","Symbol","for","TABSTER_CONFIG","getTabsterAttribute","mover","direction","MoverDirections","Both","cyclic","hasDefault","memorizeCurrent","deloser","TABSTER_SELECTED_TAB","focusable","isDefault","tabsterFor","isSelected","undefined","TabLink","setComponentTemplate","precompileTemplate","strictMode","scope","String","templateOnly","TabButton","on","TabContent","Portal","isString","x","makeTab","tabButton","tabLink","Link","TabContainer","Component","id","uniqueId","tabId","panelId","label","args","fn","Label","makeAPI","tabContainer","labelComponent","stateWaiter","buildWaiter","TabState","g","prototype","tracked","i","labelId","tabpanelId","constructor","activationMode","isAutomatic","isActive","tabValue","active","beginAsync","next","endAsync","_active","isDestroyed","isDestroying","_label","activeTab","activeLabel","handleChange","previous","onChange","Tabs","state","owner"],"mappings":"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"}
@@ -26,7 +26,6 @@
26
26
  gap: var(--zoetrope-gap, 8px);
27
27
  overflow: scroll visible;
28
28
  padding: 8px var(--zoetrope-offset, 0);
29
- scroll-behavior: smooth;
30
29
  scroll-padding-left: var(--zoetrope-offset, 0);
31
30
  scroll-snap-type: x mandatory;
32
31
  scrollbar-color: transparent transparent;
@@ -38,3 +37,9 @@
38
37
  scroll-snap-align: start;
39
38
  }
40
39
  }
40
+
41
+ @media (prefers-reduced-motion: no-preference) {
42
+ .ember-primitives__zoetrope__scroller {
43
+ scroll-behavior: smooth;
44
+ }
45
+ }
@@ -1,2 +1,2 @@
1
- export { Z as Zoetrope, Z as default } from '../index-gRO4Cvlf.js';
1
+ export { Z as Zoetrope, Z as default } from '../index-B-yrfUe9.js';
2
2
  //# sourceMappingURL=zoetrope.js.map
@@ -89,7 +89,6 @@ class Zoetrope extends Component {
89
89
  if (!shiftKey && !nextElement || shiftKey && !previousElement) {
90
90
  return;
91
91
  }
92
- event.preventDefault();
93
92
  let newTarget = null;
94
93
  if (shiftKey) {
95
94
  newTarget = previousElement;
@@ -99,9 +98,14 @@ class Zoetrope extends Component {
99
98
  if (!newTarget) {
100
99
  return;
101
100
  }
102
- newTarget?.focus({
101
+ newTarget.focus({
103
102
  preventScroll: true
104
103
  });
104
+ /** Cancelling Tab when the sibling cannot take focus traps the keyboard. */
105
+ if (target.getRootNode().activeElement !== newTarget) {
106
+ return;
107
+ }
108
+ event.preventDefault();
105
109
  const rect = getRelativeBoundingClientRect(newTarget, this.scrollerElement);
106
110
  this.scrollerElement?.scrollBy({
107
111
  left: rect.left,
@@ -130,6 +134,9 @@ class Zoetrope extends Component {
130
134
  if (macroCondition(isTesting())) {
131
135
  return "instant";
132
136
  }
137
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
138
+ return "instant";
139
+ }
133
140
  return this.args.scrollBehavior || "smooth";
134
141
  }
135
142
  scrollLeft = () => {
@@ -266,4 +273,4 @@ function getRelativeBoundingClientRect(childElement, parentElement) {
266
273
  }
267
274
 
268
275
  export { Zoetrope as Z };
269
- //# sourceMappingURL=index-gRO4Cvlf.js.map
276
+ //# sourceMappingURL=index-B-yrfUe9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-B-yrfUe9.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@ import { macroCondition, isDevelopingApp, importSync } from '@embroider/macros';
2
2
  export { Accordion } from './components/accordion.js';
3
3
  export { Avatar } from './components/avatar.js';
4
4
  export { Breadcrumb } from './components/breadcrumb.js';
5
- export { Dialog, Dialog as Modal } from './components/dialog.js';
5
+ export { CommandPalette } from './components/command-palette.js';
6
+ export { Dialog, Modal } from './components/dialog.js';
6
7
  export { Drawer } from './components/drawer.js';
7
8
  export { ExternalLink } from './components/external-link.js';
8
9
  export { Form } from './components/form.js';
@@ -11,12 +12,12 @@ export { Key, KeyCombo } from './components/keys.js';
11
12
  export { StickyFooter } from './components/layout/sticky-footer.js';
12
13
  export { Link } from './components/link.js';
13
14
  export { Menu } from './components/menu.js';
14
- export { O as OTP, a as OTPInput } from './otp-H5a4B4yu.js';
15
+ export { O as OTP, a as OTPInput } from './otp-C8c1EIw6.js';
15
16
  export { Popover } from './components/popover.js';
16
17
  export { Portal } from './components/portal.js';
17
18
  export { TARGETS as PORTALS, PortalTargets } from './components/portal-targets.js';
18
19
  export { Progress } from './components/progress.js';
19
- export { R as Rating } from './rating-BrIiwDLw.js';
20
+ export { R as Rating } from './rating-CKWhrr-L.js';
20
21
  export { Resizable, Handle as ResizableHandle, Panel as ResizablePanel } from './components/resizable.js';
21
22
  export { Scroller } from './components/scroller.js';
22
23
  export { Separator } from './components/separator.js';
@@ -26,7 +27,7 @@ export { Switch } from './components/switch.js';
26
27
  export { Toggle } from './components/toggle.js';
27
28
  export { ToggleGroup } from './components/toggle-group.js';
28
29
  export { VisuallyHidden } from './components/visually-hidden.js';
29
- export { Z as Zoetrope } from './index-gRO4Cvlf.js';
30
+ export { Z as Zoetrope } from './index-B-yrfUe9.js';
30
31
  export { link } from './helpers/link.js';
31
32
  export { service } from './helpers/service.js';
32
33
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * DANGER: this is a *barrel file*\n *\n * It forces the whole library to be loaded and all dependencies.\n *\n * If you have a small app, you probably don't want to import from here -- instead import from each sub-path.\n */\nimport { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';\n\nif (macroCondition(isDevelopingApp())) {\n importSync('./components/violations.css');\n}\n\nexport { Accordion } from './components/accordion.gts';\nexport type {\n AccordionContentExternalSignature,\n AccordionHeaderExternalSignature,\n AccordionItemExternalSignature,\n AccordionTriggerExternalSignature,\n} from './components/accordion/public.ts';\nexport { Avatar } from './components/avatar.gts';\nexport { Breadcrumb } from './components/breadcrumb.gts';\nexport { Dialog, Dialog as Modal } from './components/dialog.gts';\nexport { Drawer } from './components/drawer.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.gts';\nexport { IncrementalEach } from './components/incremental-each.gts';\nexport { Key, KeyCombo } from './components/keys.gts';\nexport { StickyFooter } from './components/layout/sticky-footer.gts';\nexport { Link } from './components/link.gts';\nexport { Menu } from './components/menu.gts';\nexport { OTP, OTPInput } from './components/one-time-password.gts';\nexport { Popover } from './components/popover.gts';\nexport { Portal } from './components/portal.gts';\nexport { PortalTargets } from './components/portal-targets.gts';\nexport { TARGETS as PORTALS } from './components/portal-targets.gts';\nexport { Progress } from './components/progress.gts';\nexport { Rating } from './components/rating.gts';\nexport {\n Resizable,\n Handle as ResizableHandle,\n Panel as ResizablePanel,\n} from './components/resizable.gts';\nexport { Scroller } from './components/scroller.gts';\nexport { Separator } from './components/separator.gts';\nexport { Shadowed } from './components/shadowed.gts';\nexport { Slider } from './components/slider.gts';\nexport { Switch } from './components/switch.gts';\nexport { Toggle } from './components/toggle.gts';\nexport { ToggleGroup } from './components/toggle-group.gts';\nexport { VisuallyHidden } from './components/visually-hidden.gts';\nexport { Zoetrope } from './components/zoetrope.ts';\nexport * from './helpers.ts';\n"],"names":["macroCondition","isDevelopingApp","importSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAIA,cAAc,CAACC,eAAe,EAAE,CAAC,EAAE;EACrCC,UAAU,CAAC,6BAA6B,CAAC;AAC3C"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * DANGER: this is a *barrel file*\n *\n * It forces the whole library to be loaded and all dependencies.\n *\n * If you have a small app, you probably don't want to import from here -- instead import from each sub-path.\n */\nimport { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';\n\nif (macroCondition(isDevelopingApp())) {\n importSync('./components/violations.css');\n}\n\nexport { Accordion } from './components/accordion.gts';\nexport type {\n AccordionContentExternalSignature,\n AccordionHeaderExternalSignature,\n AccordionItemExternalSignature,\n AccordionTriggerExternalSignature,\n} from './components/accordion/public.ts';\nexport { Avatar } from './components/avatar.gts';\nexport { Breadcrumb } from './components/breadcrumb.gts';\nexport { CommandPalette } from './components/command-palette.gts';\nexport { Dialog, Modal } from './components/dialog.gts';\nexport { Drawer } from './components/drawer.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.gts';\nexport { IncrementalEach } from './components/incremental-each.gts';\nexport { Key, KeyCombo } from './components/keys.gts';\nexport { StickyFooter } from './components/layout/sticky-footer.gts';\nexport { Link } from './components/link.gts';\nexport { Menu } from './components/menu.gts';\nexport { OTP, OTPInput } from './components/one-time-password.gts';\nexport { Popover } from './components/popover.gts';\nexport { Portal } from './components/portal.gts';\nexport { PortalTargets } from './components/portal-targets.gts';\nexport { TARGETS as PORTALS } from './components/portal-targets.gts';\nexport { Progress } from './components/progress.gts';\nexport { Rating } from './components/rating.gts';\nexport {\n Resizable,\n Handle as ResizableHandle,\n Panel as ResizablePanel,\n} from './components/resizable.gts';\nexport { Scroller } from './components/scroller.gts';\nexport { Separator } from './components/separator.gts';\nexport { Shadowed } from './components/shadowed.gts';\nexport { Slider } from './components/slider.gts';\nexport { Switch } from './components/switch.gts';\nexport { Toggle } from './components/toggle.gts';\nexport { ToggleGroup } from './components/toggle-group.gts';\nexport { VisuallyHidden } from './components/visually-hidden.gts';\nexport { Zoetrope } from './components/zoetrope.ts';\nexport * from './helpers.ts';\n"],"names":["macroCondition","isDevelopingApp","importSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAIA,cAAc,CAACC,eAAe,EAAE,CAAC,EAAE;EACrCC,UAAU,CAAC,6BAA6B,CAAC;AAC3C"}