@skdx/docs-kit 0.39.0 → 0.40.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 (142) hide show
  1. package/dist/data/angular-inputs.json +1 -1
  2. package/dist/data/api-manifest.json +1 -1
  3. package/dist/shared/contracts/accordion.js +1 -1
  4. package/dist/shared/contracts/alert.js +1 -1
  5. package/dist/shared/contracts/autocomplete.d.ts +1 -1
  6. package/dist/shared/contracts/autocomplete.js +1 -1
  7. package/dist/shared/contracts/avatar.js +1 -1
  8. package/dist/shared/contracts/breadcrumb.js +1 -1
  9. package/dist/shared/contracts/button.js +1 -1
  10. package/dist/shared/contracts/calendar.js +1 -1
  11. package/dist/shared/contracts/card.js +1 -1
  12. package/dist/shared/contracts/carousel.js +1 -1
  13. package/dist/shared/contracts/checkbox-card.js +1 -1
  14. package/dist/shared/contracts/checkbox-group.js +1 -1
  15. package/dist/shared/contracts/collapsible.js +1 -1
  16. package/dist/shared/contracts/combobox.js +1 -1
  17. package/dist/shared/contracts/command.js +1 -1
  18. package/dist/shared/contracts/dashboard-layout.js +1 -1
  19. package/dist/shared/contracts/date-picker.js +1 -1
  20. package/dist/shared/contracts/dialog.d.ts +1 -1
  21. package/dist/shared/contracts/dialog.js +1 -1
  22. package/dist/shared/contracts/drawer.d.ts +1 -1
  23. package/dist/shared/contracts/drawer.js +1 -1
  24. package/dist/shared/contracts/field.js +1 -1
  25. package/dist/shared/contracts/file-upload.js +1 -1
  26. package/dist/shared/contracts/floating-panel.js +1 -1
  27. package/dist/shared/contracts/form.js +1 -1
  28. package/dist/shared/contracts/image.js +1 -1
  29. package/dist/shared/contracts/listbox.js +1 -1
  30. package/dist/shared/contracts/loading-overlay.js +1 -1
  31. package/dist/shared/contracts/mentions.js +1 -1
  32. package/dist/shared/contracts/menu.js +1 -1
  33. package/dist/shared/contracts/menubar.js +1 -1
  34. package/dist/shared/contracts/navigation-menu.js +1 -1
  35. package/dist/shared/contracts/pagination.js +1 -1
  36. package/dist/shared/contracts/password-input.js +1 -1
  37. package/dist/shared/contracts/popover.js +1 -1
  38. package/dist/shared/contracts/progress.js +1 -1
  39. package/dist/shared/contracts/qr-code.js +1 -1
  40. package/dist/shared/contracts/radio-card.js +1 -1
  41. package/dist/shared/contracts/radio-group.js +1 -1
  42. package/dist/shared/contracts/rating.js +1 -1
  43. package/dist/shared/contracts/resizable.js +1 -1
  44. package/dist/shared/contracts/scroll-area.js +1 -1
  45. package/dist/shared/contracts/select.js +1 -1
  46. package/dist/shared/contracts/slider.js +1 -1
  47. package/dist/shared/contracts/speed-dial.js +1 -1
  48. package/dist/shared/contracts/split-button.js +1 -1
  49. package/dist/shared/contracts/stepper.js +1 -1
  50. package/dist/shared/contracts/switch.js +1 -1
  51. package/dist/shared/contracts/table.js +3 -1
  52. package/dist/shared/contracts/tabs.js +1 -1
  53. package/dist/shared/contracts/tag-group.js +1 -1
  54. package/dist/shared/contracts/tags-input.js +1 -1
  55. package/dist/shared/contracts/text-field.js +1 -1
  56. package/dist/shared/contracts/textarea.js +1 -1
  57. package/dist/shared/contracts/timeline.js +1 -1
  58. package/dist/shared/contracts/toast.js +9 -1
  59. package/dist/shared/contracts/toolbar.js +1 -1
  60. package/dist/shared/contracts/tour.js +1 -1
  61. package/dist/shared/contracts/transfer.js +1 -1
  62. package/dist/shared/contracts/tree-select.js +1 -1
  63. package/dist/shared/contracts/tree.js +1 -1
  64. package/dist/shared/contracts/types.d.ts +2 -0
  65. package/dist/shared/contracts/workspace-switcher.js +1 -1
  66. package/dist/shared/core/field-core.d.ts +2 -0
  67. package/dist/shared/core/field-core.js +1 -1
  68. package/dist/skins-src/_foundation-fallback.css +2 -1
  69. package/dist/skins-src/components/_actuator.css +1 -1
  70. package/dist/skins-src/components/_card-control.css +1 -1
  71. package/dist/skins-src/components/_collection.css +2 -2
  72. package/dist/skins-src/components/_overlay.css +1 -1
  73. package/dist/skins-src/components/_trigger.css +1 -1
  74. package/dist/skins-src/components/accordion.css +1 -1
  75. package/dist/skins-src/components/alert.css +2 -2
  76. package/dist/skins-src/components/autocomplete.css +1 -1
  77. package/dist/skins-src/components/avatar.css +1 -1
  78. package/dist/skins-src/components/badge.css +1 -1
  79. package/dist/skins-src/components/button.css +1 -1
  80. package/dist/skins-src/components/calendar.css +8 -6
  81. package/dist/skins-src/components/card.css +1 -1
  82. package/dist/skins-src/components/carousel.css +1 -1
  83. package/dist/skins-src/components/chat-composer.css +1 -1
  84. package/dist/skins-src/components/chat-message.css +1 -1
  85. package/dist/skins-src/components/checkbox-card.css +2 -2
  86. package/dist/skins-src/components/checkbox.css +1 -1
  87. package/dist/skins-src/components/clipboard.css +2 -2
  88. package/dist/skins-src/components/color-picker.css +4 -4
  89. package/dist/skins-src/components/combobox.css +2 -2
  90. package/dist/skins-src/components/command.css +1 -1
  91. package/dist/skins-src/components/dashboard-layout.css +1 -1
  92. package/dist/skins-src/components/date-field.css +2 -2
  93. package/dist/skins-src/components/dialog.css +4 -4
  94. package/dist/skins-src/components/drawer.css +9 -9
  95. package/dist/skins-src/components/editable.css +3 -3
  96. package/dist/skins-src/components/file-upload.css +4 -4
  97. package/dist/skins-src/components/floating-panel.css +1 -1
  98. package/dist/skins-src/components/forgot-password-page.css +1 -1
  99. package/dist/skins-src/components/form-section.css +1 -1
  100. package/dist/skins-src/components/form.css +12 -2
  101. package/dist/skins-src/components/image.css +1 -1
  102. package/dist/skins-src/components/kbd.css +1 -1
  103. package/dist/skins-src/components/link.css +1 -1
  104. package/dist/skins-src/components/listbox.css +1 -1
  105. package/dist/skins-src/components/mentions.css +1 -1
  106. package/dist/skins-src/components/menubar.css +2 -2
  107. package/dist/skins-src/components/navigation-menu.css +1 -1
  108. package/dist/skins-src/components/number-field.css +1 -1
  109. package/dist/skins-src/components/onboarding-checklist.css +1 -1
  110. package/dist/skins-src/components/pagination.css +2 -2
  111. package/dist/skins-src/components/password-input.css +80 -16
  112. package/dist/skins-src/components/pin-input.css +2 -2
  113. package/dist/skins-src/components/popover.css +1 -1
  114. package/dist/skins-src/components/qr-code.css +2 -2
  115. package/dist/skins-src/components/radio-card.css +1 -1
  116. package/dist/skins-src/components/reset-password-page.css +1 -1
  117. package/dist/skins-src/components/select.css +3 -3
  118. package/dist/skins-src/components/sign-in-page.css +1 -1
  119. package/dist/skins-src/components/sign-up-page.css +1 -1
  120. package/dist/skins-src/components/skeleton.css +1 -1
  121. package/dist/skins-src/components/skip-link.css +1 -1
  122. package/dist/skins-src/components/speed-dial.css +3 -3
  123. package/dist/skins-src/components/stat-card.css +6 -1
  124. package/dist/skins-src/components/stepper.css +3 -1
  125. package/dist/skins-src/components/switch.css +21 -1
  126. package/dist/skins-src/components/table.css +4 -4
  127. package/dist/skins-src/components/tags-input.css +2 -2
  128. package/dist/skins-src/components/text-field.css +26 -21
  129. package/dist/skins-src/components/text.css +1 -1
  130. package/dist/skins-src/components/textarea.css +1 -1
  131. package/dist/skins-src/components/time-field.css +2 -2
  132. package/dist/skins-src/components/toast.css +2 -2
  133. package/dist/skins-src/components/toggle-group.css +3 -3
  134. package/dist/skins-src/components/toggle.css +1 -1
  135. package/dist/skins-src/components/toolbar.css +2 -2
  136. package/dist/skins-src/components/tree-select.css +2 -2
  137. package/dist/skins-src/components/tree.css +1 -1
  138. package/dist/skins-src/components/usage-card.css +1 -1
  139. package/dist/skins-src/components/user-menu.css +1 -1
  140. package/dist/skins-src/components/verification-page.css +1 -1
  141. package/dist/skins-src/components/workspace-switcher.css +2 -2
  142. package/package.json +14 -14
@@ -1 +1 @@
1
- import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"Pagination",purpose:"A page-number navigation control that computes a collapsed model (siblings around current, boundaries at ends, ellipsis for collapsed runs) and hands it to page-link buttons via context. Also available standalone as getPaginationRange.",props:[{name:"pageCount",type:"number",required:!0,description:'Total number of pages, used to disable the "next" control on the last page.'},{name:"page",type:"number",required:!1,description:"Current page (1-based), for controlled usage."},{name:"defaultPage",type:"number",defaultValue:"1",required:!1,description:"Initial page (1-based), for uncontrolled usage."},{name:"labels",type:"{ root?: string; page?: (page: number) => string; currentPage?: (page: number) => string; first?: string; previous?: string; next?: string; last?: string }",required:!1,description:"Every string the strip speaks, each optional and falling back to its own English default. Object-valued, so a Web Components property, not attribute."},{name:"siblingCount",type:"number",defaultValue:"1",required:!1,description:"Pages shown either side of the current one in the computed page model."},{name:"boundaryCount",type:"number",defaultValue:"1",required:!1,description:"Pages always shown at each end of the computed page model, however far away the current page is."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Blocks navigation across the whole strip by ORing into every Page part's own disabled, so each item is natively disabled and drops out of the roving-focus list."},{name:"showFirstLast",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived List brackets Previous/Next with First and Last; ignored under hand composition."},{name:"showStatus",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived anatomy includes a Status live region after the List; ignored under hand composition."},{name:"density",type:"'comfortable' | (string & {})",required:!1,description:"Vertical rhythm rung, written to data-density on the root; the data-density attribute may be set directly instead."},{name:"size",type:"'md' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[{name:"page-change",detail:"number",description:"Fires with the new page number whenever it changes; never fires for the already-current page, so a consumer fetching on this event is never told to refetch it."}],parts:[{name:"List",description:'The <ul> holding the page entries, with an explicit role="list" since Safari/VoiceOver drops list semantics once the skin removes markers.'},{name:"Item",description:"One <li> wrapper around a Page, a boundary control, or an Ellipsis."},{name:"Page",description:'One page button \u2014 a <button type="button"> (role="button" host in Web Components) carrying data-skdx-pagination-item, aria-current="page" and data-state="active" while current, and data-disabled when disabled.',props:[{name:"page",type:"number",required:!0,description:"The 1-based page this button asks for when activated."},{name:"isActive",type:"boolean",required:!1,description:`Marks this page as selected (aria-current="page", data-state="active"); derived from the root's current page unless overridden for a split list/detail view.`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables this one button, excluding it from click handling and the roving-focus candidate list; ORed with the root's own disabled."}]},{name:"Previous",description:'A Page preconfigured for page - 1, labelled "Go to previous page" and disabled on page 1.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]},{name:"Next",description:'A Page preconfigured for page + 1, labelled "Go to next page" and disabled on the last page.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]},{name:"Ellipsis",description:'A presentational placeholder for a run of collapsed pages (role="presentation", aria-hidden="true", defaulting to "\u2026"), deliberately not marked data-skdx-pagination-item so the roving-focus handler can rely on that attribute alone.'},{name:"Status",description:'An optional role="status" live region announcing the page change, opt-in rather than automatic since a strip whose activation also moves focus into new content already announces it.',props:[{name:"format",type:"(page: number, pageCount: number) => string",required:!1,description:"Builds the announcement from the root's current page and page count; ignored when the consumer supplies text directly as children/slot content."}]},{name:"First",description:'A page link preconfigured for page 1, labelled "Go to first page" and disabled while page 1 is already current.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]},{name:"Last",description:'A page link preconfigured for the last page, labelled "Go to last page" and disabled while the last page is already current.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]}],slots:[{name:"default",description:"Pagination items \u2014 page links, first/previous/next/last, ellipses."}],appearance:[{attribute:"data-density",values:["comfortable"],stylesheet:"pagination.css"},{attribute:"data-size",values:["md","lg"],stylesheet:"pagination.css"}],states:["default"],controlledUncontrolled:"Supports both: `page` for controlled, `defaultPage` for uncontrolled.",formBehavior:"Not applicable \u2014 Pagination does not participate in forms.",validationBehavior:"Not applicable \u2014 Pagination carries no value and participates in no validation.",accessibility:{role:'nav (labelled "Pagination" by default)',keyboard:[{key:"ArrowLeft/ArrowRight",behavior:'Moves focus between page items (no wrap). Clamped at the ends rather than ignored: the keystroke is always consumed inside the strip, so ArrowRight on the last page link does not fall through and scroll the document. Mirrored under dir="rtl".'},{key:"Enter / Space",behavior:'Activates the focused page item. A native <button> gets both keys from the platform; a role="button" host answers them itself and calls preventDefault() on Space, whose default action on a non-button is scrolling.'},{key:"Home",behavior:"Moves focus to the first page item in the strip. A collapsed strip can be long, and the two ends are the two pages a user asks for most; without this the only way back to page 1 is one arrow press per page."},{key:"End",behavior:"Moves focus to the last page item in the strip."}],ariaAttributes:['active page link sets aria-current="page"','role="list" on the List part, set explicitly because the skin removes the markers and a marker-less list loses its semantics in Safari/VoiceOver','aria-disabled="true" on a disabled link whose role="button" host never matches :disabled',`every Page carries an aria-label, defaulting to "Go to page {page}" and to "Page {page}, current page" while active \u2014 a page button's visible label is its own digit, which announces as a number and a widget and says nothing about navigating`],namingRequirement:'Every Page needs an accessible name that is not just its own digit. The packages default it to "Go to page {page}", or "Page {page}, current page" on the active one; a consumer-supplied aria-label replaces it, which is how the four boundary controls set their own names. The wording comes from the root labels object, so a whole strip is translated in one place rather than one aria-label at a time. assertPaginationPageContract in @skdx/shared/testing rejects a link whose accessible name is empty or is its bare visible number.',focusManagement:`Every enabled page item is a tab stop; arrow keys move focus within the strip without changing the page. Not a roving tabindex: nothing writes tabindex="-1" on the unfocused items. Home and End jump to the ends of the strip. A disabled item \u2014 its own disabled, or the root's \u2014 is out of the candidate list, so the arrows step over it.`},styling:{cssParts:["root","list","item","page","first","previous","next","last","ellipsis","status"],dataAttributes:[...e,"data-state","data-disabled","data-skdx-pagination-item","data-density","data-size",{name:"data-skdx-pagination-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-pagination-control-size","--skdx-pagination-font-size","--skdx-pagination-gap"]},publicMethods:[]};export{a as paginationContract};
1
+ import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"Pagination",purpose:"A page-number navigation control that computes a collapsed model (siblings around current, boundaries at ends, ellipsis for collapsed runs) and hands it to page-link buttons via context. Also available standalone as getPaginationRange.",props:[{name:"pageCount",type:"number",required:!0,description:'Total number of pages, used to disable the "next" control on the last page.'},{name:"page",type:"number",required:!1,description:"Current page (1-based), for controlled usage."},{name:"defaultPage",type:"number",defaultValue:"1",required:!1,description:"Initial page (1-based), for uncontrolled usage."},{name:"labels",type:"{ root?: string; page?: (page: number) => string; currentPage?: (page: number) => string; first?: string; previous?: string; next?: string; last?: string }",required:!1,description:"Every string the strip speaks, each optional and falling back to its own English default. Object-valued, so a Web Components property, not attribute."},{name:"siblingCount",type:"number",defaultValue:"1",required:!1,description:"Pages shown either side of the current one in the computed page model."},{name:"boundaryCount",type:"number",defaultValue:"1",required:!1,description:"Pages always shown at each end of the computed page model, however far away the current page is."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Blocks navigation across the whole strip by ORing into every Page part's own disabled, so each item is natively disabled and drops out of the roving-focus list."},{name:"showFirstLast",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived List brackets Previous/Next with First and Last; ignored under hand composition."},{name:"showStatus",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived anatomy includes a Status live region after the List; ignored under hand composition."},{name:"density",type:"'comfortable' | (string & {})",required:!1,description:"Vertical rhythm rung, written to data-density on the root; the data-density attribute may be set directly instead."},{name:"size",type:"'md' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[{name:"page-change",detail:"number",description:"Fires with the new page number whenever it changes; never fires for the already-current page, so a consumer fetching on this event is never told to refetch it."}],parts:[{name:"List",description:'The <ul> holding the page entries, with an explicit role="list" since Safari/VoiceOver drops list semantics once the skin removes markers.'},{name:"Item",description:"One <li> wrapper around a Page, a boundary control, or an Ellipsis."},{name:"Page",description:'One page button \u2014 a <button type="button"> (role="button" host in Web Components) carrying data-skdx-pagination-item, aria-current="page" and data-state="active" while current, and data-disabled when disabled.',props:[{name:"page",type:"number",required:!0,description:"The 1-based page this button asks for when activated."},{name:"isActive",type:"boolean",required:!1,description:`Marks this page as selected (aria-current="page", data-state="active"); derived from the root's current page unless overridden for a split list/detail view.`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables this one button, excluding it from click handling and the roving-focus candidate list; ORed with the root's own disabled."}]},{name:"Previous",description:'A Page preconfigured for page - 1, labelled "Go to previous page" and disabled on page 1.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]},{name:"Next",description:'A Page preconfigured for page + 1, labelled "Go to next page" and disabled on the last page.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]},{name:"Ellipsis",description:'A presentational placeholder for a run of collapsed pages (role="presentation", aria-hidden="true", defaulting to "\u2026"), deliberately not marked data-skdx-pagination-item so the roving-focus handler can rely on that attribute alone.'},{name:"Status",description:'An optional role="status" live region announcing the page change, opt-in rather than automatic since a strip whose activation also moves focus into new content already announces it.',props:[{name:"format",type:"(page: number, pageCount: number) => string",required:!1,description:"Builds the announcement from the root's current page and page count; ignored when the consumer supplies text directly as children/slot content."}]},{name:"First",description:'A page link preconfigured for page 1, labelled "Go to first page" and disabled while page 1 is already current.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]},{name:"Last",description:'A page link preconfigured for the last page, labelled "Go to last page" and disabled while the last page is already current.',props:[{name:"label",type:"string",required:!1,frameworks:["angular"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Web Components take `aria-label` on the element through attribute passthrough.",description:"Accessible name of the page link, since projected content is often a chevron."}]}],slots:[{name:"default",description:"Pagination items \u2014 page links, first/previous/next/last, ellipses."}],appearance:[{attribute:"data-density",values:["comfortable"],stylesheet:"pagination.css"},{attribute:"data-size",values:["md","lg"],stylesheet:"pagination.css"}],states:["default"],controlledUncontrolled:"Supports both: `page` for controlled, `defaultPage` for uncontrolled.",formBehavior:"Not applicable \u2014 Pagination does not participate in forms.",validationBehavior:"Not applicable \u2014 Pagination carries no value and participates in no validation.",accessibility:{role:'nav (labelled "Pagination" by default)',keyboard:[{key:"ArrowLeft/ArrowRight",behavior:'Moves focus between page items (no wrap). Clamped at the ends rather than ignored: the keystroke is always consumed inside the strip, so ArrowRight on the last page link does not fall through and scroll the document. Mirrored under dir="rtl".'},{key:"Enter / Space",behavior:'Activates the focused page item. A native <button> gets both keys from the platform; a role="button" host answers them itself and calls preventDefault() on Space, whose default action on a non-button is scrolling.'},{key:"Home",behavior:"Moves focus to the first page item in the strip. A collapsed strip can be long, and the two ends are the two pages a user asks for most; without this the only way back to page 1 is one arrow press per page."},{key:"End",behavior:"Moves focus to the last page item in the strip."}],ariaAttributes:['active page link sets aria-current="page"','role="list" on the List part, set explicitly because the skin removes the markers and a marker-less list loses its semantics in Safari/VoiceOver','aria-disabled="true" on a disabled link whose role="button" host never matches :disabled',`every Page carries an aria-label, defaulting to "Go to page {page}" and to "Page {page}, current page" while active \u2014 a page button's visible label is its own digit, which announces as a number and a widget and says nothing about navigating`],namingRequirement:'Every Page needs an accessible name that is not just its own digit. The packages default it to "Go to page {page}", or "Page {page}, current page" on the active one; a consumer-supplied aria-label replaces it, which is how the four boundary controls set their own names. The wording comes from the root labels object, so a whole strip is translated in one place rather than one aria-label at a time. assertPaginationPageContract in @skdx/shared/testing rejects a link whose accessible name is empty or is its bare visible number.',focusManagement:`Every enabled page item is a tab stop; arrow keys move focus within the strip without changing the page. Not a roving tabindex: nothing writes tabindex="-1" on the unfocused items. Home and End jump to the ends of the strip. A disabled item \u2014 its own disabled, or the root's \u2014 is out of the candidate list, so the arrows step over it.`},styling:{cssParts:["root","list","item","page","first","previous","next","last","ellipsis","status"],dataAttributes:[...e,"data-state","data-disabled","data-skdx-pagination-item","data-density","data-size",{name:"data-skdx-pagination-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-pagination-control-size","--skdx-pagination-font-size","--skdx-pagination-gap"]},publicMethods:[],frameworkExtensions:[{framework:"webComponents",kind:"method",name:"getPage",type:"() => number",description:"The current page, resolving controlled vs. uncontrolled. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract."},{framework:"webComponents",kind:"method",name:"setPage",type:"(next: number) => void",description:"Sets the current page (if uncontrolled), and fires `page-change`. The page already shown is a no-op. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract."},{framework:"webComponents",name:"pages",type:"PaginationRangeEntry[]",description:"Collapsed page model with siblings, boundaries, and ellipsis for collapsed runs. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings.",kind:"prop"},{framework:"webComponents",kind:"method",name:"getPage",type:"() => number",description:"The current page, resolving controlled vs. uncontrolled. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract."},{framework:"webComponents",kind:"method",name:"setPage",type:"(next: number) => void",description:"Sets the current page (if uncontrolled), and fires `page-change`. The page already shown is a no-op. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract."},{framework:"webComponents",name:"pages",type:"PaginationRangeEntry[]",description:"Collapsed page model with siblings, boundaries, and ellipsis for collapsed runs. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings."}]};export{a as paginationContract};
@@ -1 +1 @@
1
- import{changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as t,setCustomValidityMethod as a}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as i}from"./styling-hooks.js";var o={name:"PasswordInput",purpose:"A password field: a TextField whose type flips between password and text, with a toggle button reporting state via aria-pressed. Forwards all TextField props plus Field and Toggle parts.",props:[{name:"visible",type:"boolean",required:!1,description:"Controlled visibility \u2014 whether the characters are revealed. Pair with the visible-change event."},{name:"defaultVisible",type:"boolean",defaultValue:"false",required:!1,description:"Initial visibility, for uncontrolled usage; starts masked, the only safe default for an onscreen credential."},{name:"value",type:"string",required:!1,description:"Controlled value, forwarded to the underlying TextField. Pair with the value-change event."},{name:"defaultValue",type:"string",required:!1,description:"Initial value, for uncontrolled usage, forwarded to the underlying TextField."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables the field and the toggle together \u2014 there is nothing to reveal in a field nobody can use."},{name:"required",type:"boolean",defaultValue:"false",required:!1,description:"Marks the field required for native form validation, forwarded to the underlying TextField."},{name:"invalid",type:"boolean",defaultValue:"false",required:!1,description:"Marks the field invalid, exposed as aria-invalid on the input."},{name:"name",type:"string",required:!1,description:"Form field name, forwarded to the underlying TextField and set on the wrapped native <input>."},{name:"revealLabel",type:"string",defaultValue:"'Show password'",required:!1,description:"Accessible name for the reveal button, an icon-only control with no text content to take a name from; the name stays stable since aria-pressed carries the state. All five packages let an aria-label written directly on the Toggle take precedence over it \u2014 React, Vue and Svelte through attribute passthrough, Angular through its aliased ariaLabel input, and Web Components through the Toggle's own label property."},{name:"showToggle",type:"boolean",defaultValue:"true",required:!1,description:"Renders the button that reveals and hides the value."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Makes the field read-only, forwarded to the input: value stays focusable, selectable and submitted, only editing is blocked. The Toggle stays operable, since revealing matters most when a credential can't be edited."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text for the derived Field, forwarded from TextField. Never a substitute for a label \u2014 the naming requirement below still applies."},{name:"clearable",type:"boolean",defaultValue:"false",required:!1,description:"Renders a button that clears the value, placed before the Toggle rather than in the trailing position the Toggle occupies."},{name:"clearLabel",type:"string",defaultValue:"'Clear'",required:!1,description:"Accessible name for the derived clear button."},{name:"getVisibilityAnnouncement",type:"(visible: boolean) => string",required:!1,description:'Words the polite live-region message announced when visibility changes \u2014 the only screen-reader confirmation the characters are now readable, since the toggle\'s own name doesn\'t change. Return an empty string to announce nothing. Defaults to "Password shown" / "Password hidden". **Property-only**: a function has no attribute form, so `<skdx-password-input>` declares it `@property({ attribute: false })`.'},{name:"size",type:"'md' | 'sm' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."},{name:"variant",type:"'filled' | 'ghost' | (string & {})",required:!1,description:"Fill style rung inherited from the underlying text field, written to data-variant on the root; the data-variant attribute may be set directly instead."},{name:"type",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"framework-host",frameworksReason:'Angular and Web Components extend the TextField class, so its type input is present but ignored: the field is always type="password" or "text" by visibility. React, Vue and Svelte compose TextField and omit the prop.',description:'Inherited from TextField and ignored: the native input is type="password" when masked and "text" when revealed.'}],events:[{name:"visible-change",detail:"boolean",description:"Fired with the new visibility whenever the characters are revealed or re-masked, controlled or not."},{name:"value-change",description:"Fired with the new value as the user types, forwarded from the underlying TextField.",detail:e("string")}],parts:[{name:"Field",lightDom:!0,description:"The native input; its type is owned by the root and flips with visibility, so it isn't settable from outside.",props:[{name:"size",type:"'md' | 'sm' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the input; the data-size attribute may be set directly instead."},{name:"variant",type:"'filled' | 'ghost' | (string & {})",required:!1,description:"Fill style rung inherited from the underlying text field, written to data-variant on the input; the data-variant attribute may be set directly instead."}]},{name:"Toggle",description:'The reveal button, carrying aria-pressed for the state and data-state for CSS; a `<button type="button">` and its own tab stop, never `tabindex="-1"`.'}],slots:[{name:"default",description:"The field's body \u2014 a Field and a Toggle."}],appearance:[{attribute:"data-size",values:["md","sm","lg"],stylesheet:"password-input.css"},{attribute:"data-variant",values:["filled","ghost"],stylesheet:"password-input.css"}],states:["hidden","visible","disabled","invalid"],controlledUncontrolled:"Two independent axes: value (inherited from TextField) and visible, each starting from its default* prop and switching to controlled when the matching prop is passed.",formBehavior:"Whatever TextField does, unchanged: a real <input> that submits under its name and participates in form reset. The type flip does not affect submission. A form reset restores `defaultValue` **and re-masks the field back to `defaultVisible`**, firing visible-change, so a credential isn't left on screen after reset. **The plaintext value is never reflected to an attribute, in any package** \u2014 not `value`, not `data-*`, not `aria-*` \u2014 since that would put a credential into the serialised DOM, reachable via `outerHTML`, a DOM snapshot or a session replay. The one exception is React, which maintains the `value` attribute on every `<input>` it controls regardless of type, since that's what a native input's form reset restores from.",validationBehavior:"Native constraint validation forwarded from TextField (required, minLength, pattern, and the rest). No password-strength rules of its own: strength is a product policy, and a library guessing at it would be wrong more often than right.",accessibility:{role:"none \u2014 the Field is a native input and the Toggle a native button",keyboard:[{key:"Enter / Space (on the Toggle)",behavior:"Reveals or re-masks the characters."},{key:"Tab",behavior:'Moves from the field to the toggle. The toggle MUST be a tab stop of its own and MUST NOT carry tabindex="-1": revealing the characters is the only way a keyboard-only user can check what they typed, making removal a WCAG 2.1.1 failure.'}],ariaAttributes:["aria-pressed on the Toggle reflecting whether the characters are currently revealed \u2014 a swapped eye icon says nothing to a screen reader","aria-invalid on the input while invalid","The input keeps its type toggled between password and text, so browser and password-manager behaviour follows the real state rather than a CSS approximation","aria-readonly is not set: the field carries the native readonly attribute, which the platform already maps to the same state"],focusManagement:"Toggling does not move focus: the caret stays where it was, so revealing mid-entry does not cost the user their place \u2014 the reason every package flips the input's type in place rather than swapping in a second element. The toggle is a separate tab stop after the field, and every package must leave it in the tab order.",namingRequirement:'The Toggle must be named ("Show password"), and the name should stay stable while aria-pressed carries the state \u2014 a label that flips between "Show" and "Hide" duplicates what aria-pressed already announces. The field itself is named by the surrounding Field/Label like any TextField.'},styling:{cssParts:["root","toggle","field","input"],dataAttributes:[...i,"data-state","data-disabled","data-invalid","data-size","data-variant",{name:"data-skdx-password-input-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[...t,a,{name:"setVisible",signature:"(visible: boolean): void",description:"Shows or masks the characters from code, firing the visible-change event exactly as pressing the Toggle does; a no-op while the field is disabled."}]};export{o as passwordInputContract};
1
+ import{changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as t,setCustomValidityMethod as a}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as r}from"./styling-hooks.js";var s={name:"PasswordInput",purpose:"A password field: a TextField whose type flips between password and text, with a toggle button reporting state via aria-pressed. Forwards all TextField props plus Field and Toggle parts.",props:[{name:"visible",type:"boolean",required:!1,description:"Controlled visibility \u2014 whether the characters are revealed. Pair with the visible-change event."},{name:"defaultVisible",type:"boolean",defaultValue:"false",required:!1,description:"Initial visibility, for uncontrolled usage; starts masked, the only safe default for an onscreen credential."},{name:"value",type:"string",required:!1,description:"Controlled value, forwarded to the underlying TextField. Pair with the value-change event."},{name:"defaultValue",type:"string",required:!1,description:"Initial value, for uncontrolled usage, forwarded to the underlying TextField."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables the field and the toggle together \u2014 there is nothing to reveal in a field nobody can use."},{name:"required",type:"boolean",defaultValue:"false",required:!1,description:"Marks the field required for native form validation, forwarded to the underlying TextField."},{name:"invalid",type:"boolean",defaultValue:"false",required:!1,description:"Marks the field invalid, exposed as aria-invalid on the input."},{name:"name",type:"string",required:!1,description:"Form field name, forwarded to the underlying TextField and set on the wrapped native <input>."},{name:"revealLabel",type:"string",defaultValue:"'Show password'",required:!1,description:"Accessible name for the reveal button, an icon-only control with no text content to take a name from; the name stays stable since aria-pressed carries the state. All five packages let an aria-label written directly on the Toggle take precedence over it \u2014 React, Vue and Svelte through attribute passthrough, Angular through its aliased ariaLabel input, and Web Components through the Toggle's own label property."},{name:"showToggle",type:"boolean",defaultValue:"true",required:!1,description:"Renders the button that reveals and hides the value."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Makes the field read-only, forwarded to the input: value stays focusable, selectable and submitted, only editing is blocked. The Toggle stays operable, since revealing matters most when a credential can't be edited."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text for the derived Field, forwarded from TextField. Never a substitute for a label \u2014 the naming requirement below still applies."},{name:"clearable",type:"boolean",defaultValue:"false",required:!1,description:"Renders a button that clears the value, placed before the Toggle rather than in the trailing position the Toggle occupies."},{name:"clearLabel",type:"string",defaultValue:"'Clear'",required:!1,description:"Accessible name for the derived clear button."},{name:"getVisibilityAnnouncement",type:"(visible: boolean) => string",required:!1,description:'Words the polite live-region message announced when visibility changes \u2014 the only screen-reader confirmation the characters are now readable, since the toggle\'s own name doesn\'t change. Return an empty string to announce nothing. Defaults to "Password shown" / "Password hidden". **Property-only**: a function has no attribute form, so `<skdx-password-input>` declares it `@property({ attribute: false })`.'},{name:"size",type:"'md' | 'sm' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."},{name:"variant",type:"'filled' | 'ghost' | (string & {})",required:!1,description:"Fill style rung inherited from the underlying text field, written to data-variant on the root; the data-variant attribute may be set directly instead."},{name:"type",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"framework-host",frameworksReason:'Angular and Web Components extend the TextField class, so its type input is present but ignored: the field is always type="password" or "text" by visibility. React, Vue and Svelte compose TextField and omit the prop.',description:'Inherited from TextField and ignored: the native input is type="password" when masked and "text" when revealed.'}],events:[{name:"visible-change",detail:"boolean",description:"Fired with the new visibility whenever the characters are revealed or re-masked, controlled or not."},{name:"value-change",description:"Fired with the new value as the user types, forwarded from the underlying TextField.",detail:e("string")}],parts:[{name:"Field",lightDom:!0,description:"The native input; its type is owned by the root and flips with visibility, so it isn't settable from outside.",props:[{name:"size",type:"'md' | 'sm' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the input; the data-size attribute may be set directly instead."},{name:"variant",type:"'filled' | 'ghost' | (string & {})",required:!1,description:"Fill style rung inherited from the underlying text field, written to data-variant on the input; the data-variant attribute may be set directly instead."}]},{name:"Toggle",description:'The reveal button, carrying aria-pressed for the state and data-state for CSS; a `<button type="button">` and its own tab stop, never `tabindex="-1"`.'}],slots:[{name:"default",description:"The field's body \u2014 a Field and a Toggle."},{name:"leading",description:"Content before the password field: a leading icon or other decoration. React and Angular expose the corresponding leading render prop/template."}],appearance:[{attribute:"data-size",values:["md","sm","lg"],stylesheet:"password-input.css"},{attribute:"data-variant",values:["filled","ghost"],stylesheet:"password-input.css"}],states:["hidden","visible","disabled","invalid"],controlledUncontrolled:"Two independent axes: value (inherited from TextField) and visible, each starting from its default* prop and switching to controlled when the matching prop is passed.",formBehavior:"Whatever TextField does, unchanged: a real <input> that submits under its name and participates in form reset. The type flip does not affect submission. A form reset restores `defaultValue` **and re-masks the field back to `defaultVisible`**, firing visible-change, so a credential isn't left on screen after reset. **The plaintext value is never reflected to an attribute, in any package** \u2014 not `value`, not `data-*`, not `aria-*` \u2014 since that would put a credential into the serialised DOM, reachable via `outerHTML`, a DOM snapshot or a session replay. The one exception is React, which maintains the `value` attribute on every `<input>` it controls regardless of type, since that's what a native input's form reset restores from.",validationBehavior:"Native constraint validation forwarded from TextField (required, minLength, pattern, and the rest). No password-strength rules of its own: strength is a product policy, and a library guessing at it would be wrong more often than right.",accessibility:{role:"none \u2014 the Field is a native input and the Toggle a native button",keyboard:[{key:"Enter / Space (on the Toggle)",behavior:"Reveals or re-masks the characters."},{key:"Tab",behavior:'Moves from the field to the toggle. The toggle MUST be a tab stop of its own and MUST NOT carry tabindex="-1": revealing the characters is the only way a keyboard-only user can check what they typed, making removal a WCAG 2.1.1 failure.'}],ariaAttributes:["aria-pressed on the Toggle reflecting whether the characters are currently revealed \u2014 a swapped eye icon says nothing to a screen reader","aria-invalid on the input while invalid","The input keeps its type toggled between password and text, so browser and password-manager behaviour follows the real state rather than a CSS approximation","aria-readonly is not set: the field carries the native readonly attribute, which the platform already maps to the same state"],focusManagement:"Toggling does not move focus: the caret stays where it was, so revealing mid-entry does not cost the user their place \u2014 the reason every package flips the input's type in place rather than swapping in a second element. The toggle is a separate tab stop after the field, and every package must leave it in the tab order.",namingRequirement:'The Toggle must be named ("Show password"), and the name should stay stable while aria-pressed carries the state \u2014 a label that flips between "Show" and "Hide" duplicates what aria-pressed already announces. The field itself is named by the surrounding Field/Label like any TextField.'},styling:{cssParts:["root","toggle","field","input"],dataAttributes:[...r,"data-state","data-disabled","data-invalid","data-size","data-variant",{name:"data-skdx-password-input-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[...t,a,{name:"setVisible",signature:"(visible: boolean): void",description:"Shows or masks the characters from code, firing the visible-change event exactly as pressing the Toggle does; a no-op while the field is disabled."}],frameworkExtensions:[{framework:"webComponents",part:"Field",name:"autoComplete",type:"string",description:"Native <input> auto-complete binding on webComponents PasswordInputField. Native `autocomplete` hint for password managers, defaulting to `current-password`. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"angular",part:"Field",name:"autocomplete",type:"string",description:"Native <input> autocomplete binding on angular PasswordInputField. Native `<input>` `autocomplete` hint for password managers. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"angular",part:"Field",name:"maxLength",type:"number",description:"Native <input> max-length binding on angular PasswordInputField. Native `<input>` `maxlength` \u2014 the longest value the field accepts, reported as `tooLong`; the browser caps typing and truncates pastes. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"webComponents",part:"Field",name:"maxLength",type:"number | undefined",description:"Native <input> max-length binding on webComponents PasswordInputField. Native `<input>` `maxlength` \u2014 longest value the field accepts, reported as `tooLong`; the browser caps typing and truncates pastes. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"angular",part:"Field",name:"minLength",type:"number",description:"Native <input> min-length binding on angular PasswordInputField. Native `<input>` `minlength` \u2014 the shortest value constraint validation accepts, reported as `tooShort`; pair with `required` to also catch an empty field. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"webComponents",part:"Field",name:"minLength",type:"number | undefined",description:"Native <input> min-length binding on webComponents PasswordInputField. Native `<input>` `minlength` \u2014 shortest value constraint validation accepts, reported as `tooShort`; only fires after a user edit. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"angular",part:"Field",name:"pattern",type:"string",description:"Native <input> pattern binding on angular PasswordInputField. Native `<input>` `pattern` \u2014 a regular expression the value has to match\nfor constraint validation. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"webComponents",part:"Field",name:"pattern",type:"string | undefined",description:"Native <input> pattern binding on webComponents PasswordInputField. Native `<input>` `pattern` \u2014 a regular expression the value has to match for constraint validation. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"react",name:"form",type:"string | undefined",description:"Native <input> form binding on react PasswordInput. Id of the `<form>` this field submits with, put on the wrapped native `<input>` as its `form` attribute. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"angular",name:"form",type:"string | undefined",description:"Native <input> form binding on angular PasswordInput. Id of the `<form>` this field submits with, put on the wrapped `<input>` as its `form` attribute. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.",kind:"prop"},{framework:"webComponents",name:"form",type:"HTMLFormElement | null",description:'Native <input> form binding on webComponents PasswordInput. Re-associates with `value`\'s form via the native `form="id"` attribute, or clears it for a target with no `id`. This wrapper declares the native binding explicitly; other wrappers inherit native-element attributes rather than duplicate each DOM attribute as a component prop.',kind:"prop"},{framework:"svelte",kind:"method",name:"validationMessage",type:"() => string",description:"The message the browser would show for the current validity; `''` while valid. Svelte exports a function through bind:this to read live native input validation state; Vue and Web Components expose the corresponding live property, and native input references provide the platform API."},{framework:"svelte",kind:"method",name:"validity",type:"() => ValidityState | null",description:"The wrapped input's own `ValidityState`, live; `null` before the input mounts. Svelte exports a function through bind:this to read live native input validation state; Vue and Web Components expose the corresponding live property, and native input references provide the platform API."},{framework:"vue",name:"inputEl",type:"unknown",description:"The wrapped native input element, for focus and measurement. This is a reference to the underlying native element for integration with platform APIs; the other wrappers hand off elements through their framework ref/context mechanisms instead of this property spelling.",kind:"prop"},{framework:"react",name:"leading",type:"React.ReactNode",description:"Renders a leading `TextFieldSlot` holding this content, before the input; ignored when `children` is given. This wrapper receives projected content as a render prop, Snippet or TemplateRef. Other wrappers supply the matching named slot or default content of the composed part; the property is framework rendering syntax.",kind:"prop"},{framework:"svelte",name:"leading",type:"Snippet | undefined",description:"Renders a leading `Slot` before the input. Ignored while `children` is given. This wrapper receives projected content as a render prop, Snippet or TemplateRef. Other wrappers supply the matching named slot or default content of the composed part; the property is framework rendering syntax.",kind:"prop"},{framework:"angular",name:"leading",type:"TemplateRef<unknown>",description:"Content drawn into a derived leading Slot, when deriving from no children. This wrapper receives projected content as a render prop, Snippet or TemplateRef. Other wrappers supply the matching named slot or default content of the composed part; the property is framework rendering syntax.",kind:"prop"},{framework:"webComponents",name:"validationMessage",type:"string",description:"The message the browser would show for the current validity; `''` when valid. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings.",kind:"prop"},{framework:"webComponents",name:"validity",type:"ValidityState | null",description:"The control's `ValidityState`, live; use it for custom error rendering\ninstead of parsing the browser message. `null` before any input part mounts. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings.",kind:"prop"},{framework:"svelte",kind:"method",name:"validationMessage",type:"() => string",description:"The message the browser would show for the current validity; `''` while valid. Svelte exports a function through bind:this to read live native input validation state; Vue and Web Components expose the corresponding live property, and native input references provide the platform API."},{framework:"svelte",kind:"method",name:"validity",type:"() => ValidityState | null",description:"The wrapped input's own `ValidityState`, live; `null` before the input mounts. Svelte exports a function through bind:this to read live native input validation state; Vue and Web Components expose the corresponding live property, and native input references provide the platform API."},{framework:"vue",name:"inputEl",type:"unknown",description:"The wrapped native input element, for focus and measurement. This is a reference to the underlying native element for integration with platform APIs; the other wrappers hand off elements through their framework ref/context mechanisms instead of this property spelling."},{framework:"react",name:"leading",type:"React.ReactNode",description:"Renders a leading `TextFieldSlot` holding this content, before the input; ignored when `children` is given. This wrapper receives projected content as a render prop, Snippet or TemplateRef. Other wrappers supply the matching named slot or default content of the composed part; the property is framework rendering syntax."},{framework:"svelte",name:"leading",type:"Snippet | undefined",description:"Renders a leading `Slot` before the input. Ignored while `children` is given. This wrapper receives projected content as a render prop, Snippet or TemplateRef. Other wrappers supply the matching named slot or default content of the composed part; the property is framework rendering syntax."},{framework:"angular",name:"leading",type:"TemplateRef<unknown>",description:"Content drawn into a derived leading Slot, when deriving from no children. This wrapper receives projected content as a render prop, Snippet or TemplateRef. Other wrappers supply the matching named slot or default content of the composed part; the property is framework rendering syntax."},{framework:"vue",name:"validationMessage",type:"unknown",description:"The browser's validation message for the current validity; `''` when valid. This wrapper exposes the underlying native input validity or validationMessage as a live property; Svelte exposes callable instance accessors and other wrappers provide their native input ref/validation methods.",kind:"prop"},{framework:"webComponents",name:"validationMessage",type:"string",description:"The message the browser would show for the current validity; `''` when valid. This wrapper exposes the underlying native input validity or validationMessage as a live property; Svelte exposes callable instance accessors and other wrappers provide their native input ref/validation methods."},{framework:"vue",name:"validity",type:"unknown",description:"The wrapped native input's `ValidityState`; `null` before an input has mounted. This wrapper exposes the underlying native input validity or validationMessage as a live property; Svelte exposes callable instance accessors and other wrappers provide their native input ref/validation methods.",kind:"prop"},{framework:"webComponents",name:"validity",type:"ValidityState | null",description:"The control's `ValidityState`, live; use it for custom error rendering\ninstead of parsing the browser message. `null` before any input part mounts. This wrapper exposes the underlying native input validity or validationMessage as a live property; Svelte exposes callable instance accessors and other wrappers provide their native input ref/validation methods."}]};export{s as passwordInputContract};
@@ -1 +1 @@
1
- import{arrowPart as t,autoFocusEvents as o,dismissalEvents as n,forceMountProp as a,openChangeCompleteEvent as r,popperContentProps as i,popperMeasurementProperties as s,portalPart as e}from"./overlay-parts.js";import{PART_DATA_ATTRIBUTES as l}from"./styling-hooks.js";var c={name:"Popover",purpose:'A floating panel positioned via floating-ui, anchored to a trigger, and dismissed on Escape or outside pointer-down. Derives trigger, portal, and panel anatomy from trigger, title, description, side, align, and showArrow props; manual composition via primitives/popover. Two axes widen it past the click-opened dialog: openOn="hover" reveals the panel on pointer enter after openDelay and hides it after closeDelay, and role="tooltip" makes the panel an inert, non-focusable bubble the Trigger points aria-describedby at.',props:[{name:"trigger",type:"ReactElement | Snippet | TemplateRef<unknown>",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:`React, Svelte, and Angular take this as a prop. Vue and Web Components use named #trigger slot and slot="trigger" instead. Only React merges onto the consumer's element via asChild; others render their own button.`,description:"The trigger for the derived anatomy. Content model varies by framework: React passes a whole element (merged via asChild); Svelte and Angular pass only button inner content."},{name:"title",type:"string",required:!1,description:"The derived panel's accessible name, rendered as the Title part."},{name:"description",type:"string",required:!1,description:"The derived panel's accessible description, rendered as the Description part."},{name:"openOn",type:"'click' | 'hover'",defaultValue:"'click'",required:!1,description:'Which gesture reveals the panel. "click" toggles it from the Trigger button and keeps it open until dismissed. "hover" opens it on pointer enter after openDelay and closes it after closeDelay once the pointer has left both Trigger and Content, so the pointer can travel onto the panel \u2014 the Hoverable requirement of WCAG 2.2 SC 1.4.13. Keyboard focus of the Trigger also opens a hover panel under role="tooltip"; under role="dialog" hover is the only activation there is, so the Trigger must carry an href (or its own tabindex) for keyboard and touch users to reach the same information.'},{name:"role",type:"'dialog' | 'tooltip'",defaultValue:"'dialog'",required:!1,description:'What the panel is. "dialog" is an operable panel: it can be named by Title, hold buttons and links, take focus, and honour modal. "tooltip" is an inert bubble \u2014 Content renders role="tooltip", takes no focus and traps none, Tab passes straight through it, the Trigger points aria-describedby at it while open, Escape and pointer-leave and blur all close it, modal is forced to "none" whatever is passed, and data-state reports delayed-open or instant-open rather than open.'},{name:"side",type:"'top' | 'right' | 'bottom' | 'left' | 'start' | 'end'",defaultValue:"'bottom'",required:!1,description:`Which side of the trigger the derived panel opens on \u2014 forwarded to the underlying Content part. Set Content's own side directly under full manual composition. A tooltip bubble conventionally sits above its trigger, so pass side="top" under role="tooltip".`},{name:"align",type:"'start' | 'center' | 'end'",defaultValue:"'center'",required:!1,description:"Alignment of the derived panel along side \u2014 forwarded to the underlying Content part."},{name:"showArrow",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived panel renders an Arrow part. Gates element creation only; size and position are CSS once created."},{name:"open",type:"boolean",required:!1,description:"Controlled open state. Pair with the open-change event."},{name:"defaultOpen",type:"boolean",defaultValue:"false",required:!1,description:"Initial open state, for uncontrolled usage."},{name:"openDelay",type:"number",defaultValue:"700",required:!1,description:`Delay in milliseconds before a hover popover opens on pointer enter. Read only under openOn="hover"; under role="tooltip" an ancestor Provider's delayDuration applies instead unless this is set, and the Provider's skipDelayDuration can waive the delay entirely for a panel opened just after a sibling closed.`},{name:"closeDelay",type:"number",defaultValue:"300",required:!1,description:`Delay in milliseconds before a hover popover closes once the pointer has left both Trigger and Content. Read only under openOn="hover". Under role="tooltip" an ancestor Provider's closeDelay applies instead unless this is set (0 with no Provider), and while the bubble is hoverable the effective delay is max(closeDelay, 150), protecting the crossing time WCAG 2.2 SC 1.4.13 requires; a bubble with disableHoverableContent uses the value exactly.`},{name:"disableHoverableContent",type:"boolean",defaultValue:"false",required:!1,description:'Whether the pointer moving from Trigger onto Content closes the panel instead of keeping it open. Overrides an ancestor Provider. Defaults to false so the pointer can travel onto the panel, which is the Hoverable requirement of WCAG 2.2 SC 1.4.13; set true only for a bubble that must stay pointer-transparent. Read only under openOn="hover".'},{name:"href",type:"string",required:!1,frameworks:["vue","svelte","angular","webComponents"],exception:"as-directive",frameworksReason:"React's trigger is a whole element supplied through asChild, including its own href, so React declares no member for it; the other four render the anchor themselves and need the destination as a declared property.",description:`Destination for the derived Trigger's anchor. A hover popover under role="dialog" opens on pointer enter and on nothing else, so this link is the only route keyboard and touch users have to the same information; omitting it renders an <a> that is neither in tab order nor a link.`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Whether the panel is suppressed. While true it never opens: the pointer entering Trigger does nothing, aria-describedby is never set, data-state stays closed and open-change never fires. A panel already open when this turns true closes without an event."},{name:"modal",type:"'none' | 'modal' | 'trap-focus'",defaultValue:"'none'",required:!1,description:`How the open popover constrains the page across three independent axes: focus containment, outside-interaction blocking, and page-scroll lock. 'none' (default) applies none of them \u2014 focus stays in the page, the page scrolls, the background stays interactive. 'trap-focus' cycles Tab inside Content and makes the rest of the document inert, leaving page scroll alone. 'modal' applies all three, like Dialog and Drawer, and traps the user. Anything other than 'none' requires a Close part inside Content. Forced to 'none' under role="tooltip", whatever is passed: a bubble that cannot take focus has nothing to contain.`}],events:[{name:"open-change",detail:"boolean",description:"Fired with the new open state whenever it changes, controlled or not."},r("popover"),...n("popover",{outsideNote:'Under role="tooltip" the bubble closes on pointer-leave and on blur rather than on where an outside interaction landed, so the three outside events are notification-only there: the payload stays cancelable, and preventing default changes nothing. escape-key-down is the exception and is listened for on the document while open, so a hover-revealed bubble is dismissable with focus elsewhere (WCAG 2.2 SC 1.4.13).'}),...o("popover")],parts:[{name:"Provider",description:'Optional ancestor coordinating hover-delay timing across every descendant popover with role="tooltip". Renders no element of its own, and a descendant with its own openDelay, closeDelay or disableHoverableContent overrides it. Without one each tooltip falls back to its own values.',props:[{name:"delayDuration",type:"number",defaultValue:"700",required:!1,description:"Delay in milliseconds before a descendant tooltip opens on hover. Each descendant can override it with its own openDelay."},{name:"skipDelayDuration",type:"number",defaultValue:"300",required:!1,description:'Window in milliseconds after a tooltip closes during which sibling tooltips open instantly, so sweeping the pointer along a row of triggers does not re-pay the open delay each time. An instantly-opened bubble reports data-state="instant-open".'},{name:"disableHoverableContent",type:"boolean",defaultValue:"false",required:!1,description:"Whether descendant bubbles ignore the pointer entirely, so moving off Trigger closes them. Defaults to false: the pointer can travel from Trigger into Content, which is what WCAG 2.2 SC 1.4.13 Hoverable requires. Each descendant can override it."},{name:"closeDelay",type:"number",defaultValue:"0",required:!1,description:"Delay in milliseconds before a descendant tooltip closes after the pointer leaves its Trigger. Each descendant can override it, and a hoverable bubble applies the 150 ms floor, so this value can only lengthen the window."}]},{name:"Trigger",description:'The button that opens the popover. Also the default positioning anchor, unless an Anchor part overrides it. Under openOn="hover" it reveals the panel on pointer enter and keeps focus throughout \u2014 the panel never steals it \u2014 and the recipe renders it as an anchor when href is set, which is what makes a hover panel reachable without a pointer. Under role="tooltip" it carries aria-describedby at the open bubble and must hold its own accessible name.'},{name:"Anchor",description:"Optional alternative positioning anchor \u2014 Content floats against this element instead of Trigger, for cases where the thing you click and the thing you point at differ."},{...e,description:`${e.description} Framework-conditional: React, Vue, Svelte ship a Portal component. Web Components ships <skdx-popover-portal> as a bare subclass (positioning uses strategy: 'fixed', nothing to escape). Angular uses [skdxPortal] directive. container is reachable everywhere, but in Web Components only as a property \u2014 DOM nodes cannot cross HTML attributes.`},{name:"Content",description:'The floating panel (role="dialog"). Owns positioning, dismissal interceptors (escape-key-down, pointer-down-outside, etc.), focus overrides, and modality (focus trap, inert barrier, scroll lock as modal requests). Under role="tooltip" it renders role="tooltip" instead and is inert: it moves no focus, traps none, holds nothing tabbable, and Tab passes through it as though it were closed \u2014 put buttons and links in a role="dialog" panel. Under openOn="hover" it accepts the pointer by default and reflects data-hoverable while it does, so the pointer can rest on the panel without closing it; disableHoverableContent drops the attribute and ignores the pointer entirely.',props:[...i(),a,{name:"arrowPadding",type:"number",defaultValue:"8",required:!1,description:"Clearance in pixels between the Arrow and panel edges, fed to the floating layer's arrow middleware. Prevents the arrow from sitting over rounded corners when shifted by collision avoidance. Arrow size stays in CSS; only clearance is computed. Default 8 matches the shipped skin's corner radius."},{name:"hideWhenDetached",type:"boolean",defaultValue:"false",required:!1,description:"Hide the panel while its anchor has scrolled out of view inside a clipping ancestor. The panel is hidden (visibility: hidden, pointer-events: none), never unmounted or closed \u2014 unmounting on scroll would tear down the focus trap and move focus unexpectedly. Scrolling the anchor back into view shows it again. Off by default because the extra overflow measurement on every reposition answers a question an anchor outside a scroll container never poses."},{name:"initialFocus",type:"string",required:!1,description:"CSS selector for the element that should receive focus when the popover opens, resolved with querySelector inside Content. Selector form matches Dialog/Drawer. Live only when modal contains focus ('trap-focus' or 'modal'); a 'none' popover never moves focus in. Applies only when open-auto-focus was not prevented. Falls back to the first tabbable element in Content, then Content itself."},{name:"finalFocus",type:"string",required:!1,description:"CSS selector for the element focus should be sent to when the popover closes, resolved at document level. Unlike initialFocus, this is live in every modality: Escape returns focus in all modes. Applies only when close-auto-focus was not prevented. Falls back to the trigger (or whatever was focused before opening) when the selector matches nothing."}]},{name:"Title",description:"The popover's accessible name. Generates an id and wires it onto Content's aria-labelledby while mounted. Optional in the sense that aria-label can replace it, never in the sense the panel may go unnamed."},{name:"Description",description:"Supporting text for the panel, wired onto Content's aria-describedby the same way and only while mounted."},t,{name:"Close",description:"A button inside Content that closes the popover."}],slots:[{name:"trigger",description:"Trigger's content \u2014 the button (or, with href, the anchor) that reveals the popover and anchors its position."},{name:"default",description:"The panel's body content, including a tooltip bubble's label text. In manual composition, Content's body from Title, Description, Arrow, and Close."},{name:"title",description:"Title's content \u2014 the popover's accessible name."},{name:"description",description:"Description's content \u2014 the popover's accessible description."}],states:["closed","open","delayed-open","instant-open"],controlledUncontrolled:"Stateless by default (uncontrolled): starts at defaultOpen and manages its own state internally. Passing open switches to controlled.",formBehavior:'Not applicable \u2014 Popover participates in no form; Trigger and Close are type="button".',validationBehavior:"Not applicable.",accessibility:{role:'dialog, or tooltip under role="tooltip"',keyboard:[{key:"Escape",behavior:'Requests close (cancelable via escape-key-down), refocuses Trigger. Under role="tooltip" the listener is on the document, so a hover-revealed bubble is dismissable even when focus is elsewhere (WCAG 2.2 SC 1.4.13).'},{key:"Tab / Shift+Tab",behavior:`Cycles focus within Content only while modal contains focus ('trap-focus' or 'modal'); leaves the panel into the page when modal is 'none'. Under role="tooltip" it is never intercepted \u2014 the bubble holds nothing tabbable and Tab moves through the page as if it were closed.`}],ariaAttributes:['aria-haspopup="dialog", aria-expanded, aria-controls on Trigger',"Trigger's aria-controls points at Content's id","aria-labelledby on Content points at Title's id, aria-describedby at Description's \u2014 each only while mounted","While focus-containing ('trap-focus' or true), everything outside is inert; focus is trapped","Focus-containing popovers must render a Close part \u2014 Escape is unavailable to touch screen-reader users",`Under role="tooltip" the Trigger instead points aria-describedby at Content's id, only while open, and neither aria-haspopup nor aria-expanded is emitted. Content must then hold text only: aria-describedby flattens every role and boundary inside it, so rich or interactive content belongs in a role="dialog" panel.`,"A hover popover accepts the pointer by default: it may leave Trigger and rest on the panel without it closing, which is the Hoverable requirement of WCAG 2.2 SC 1.4.13.","A hover popover is unreachable on touch-only input \u2014 there is no long-press path, and platform gestures take precedence. Anything it conveys must also be available from a control touch can reach, which is what the Trigger's href is for.","A tooltip bubble must contain no Trigger of its own. Nesting is unsupported: Escape is claimed by the top dismiss layer only, so an inner panel would swallow the outer one's dismissal."],focusManagement:`Traps focus in Content only when modal contains it ('trap-focus' or 'modal'); always returns focus to Trigger on Escape. Announces both moves as cancelable open-auto-focus / close-auto-focus events. Content's initialFocus and finalFocus selectors redirect these moves (applied only when events not prevented). initialFocus is inert while modal is 'none'; finalFocus is live in every mode. Under role="tooltip" the panel moves no focus at all in either direction \u2014 Trigger keeps it and the bubble only appears and disappears around it \u2014 so initialFocus goes unread and modal is forced to 'none'. A hover panel under role="dialog" likewise never pulls the caret off the trigger; if its content has to be operated, open it on click instead.`,namingRequirement:'Content is a role="dialog" and must be named: render a Title or set aria-label on Content. Unnamed panels announce as bare "dialog". Under role="tooltip" the obligation moves to the Trigger, which must carry its own accessible name \u2014 visible text, or an aria-label when icon-only. The bubble is wired as a description, and a description is not a name: an icon button whose only text lives in its tooltip is an unnamed button, and Content is never a naming source. A hover panel needs a focusable Trigger for the same reason \u2014 an <a> with no href is neither in tab order nor a link.'},styling:{cssParts:["trigger","anchor","content","title","description","arrow","close"],dataAttributes:[...l,"data-state","data-side","data-align","data-hoverable",{name:"data-skdx-popover-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:"Marks a node the Web Components port generated for itself rather than one the consumer authored; the other four frameworks re-render their derived anatomy declaratively, so they never need this disambiguation."}],cssCustomProperties:s("popover")},publicMethods:[{name:"reposition",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React's root forwards no imperative handle and renders no element, so there is no instance to hang a method off \u2014 usePopoverContext is React's binding of this member and it is declared under contextApi.",signature:"() => void",description:"Recomputes the open panel's position immediately. Needed when the anchor moves for a reason the DOM never reports (CSS transform finishing, virtual list reflow, etc.). No-op while closed or unmounted."}],contextApi:[{name:"reposition",signature:"() => void",description:"Recomputes the open panel's position immediately for an anchor that moved without observers noticing.",accessors:{react:"usePopoverContext('MyComponent').reposition",vue:"popoverRef.value.reposition() \u2014 defineExpose'd on SkdxPopover",svelte:"popover.reposition() \u2014 the instance export on Popover",angular:"skdxPopoverRef.reposition()",webComponents:"document.querySelector('skdx-popover').reposition()"}}]};export{c as popoverContract};
1
+ import{arrowPart as t,autoFocusEvents as o,dismissalEvents as n,forceMountProp as a,openChangeCompleteEvent as r,popperContentProps as i,popperMeasurementProperties as s,portalPart as e}from"./overlay-parts.js";import{PART_DATA_ATTRIBUTES as l}from"./styling-hooks.js";var c={name:"Popover",purpose:'A floating panel positioned via floating-ui, anchored to a trigger, and dismissed on Escape or outside pointer-down. Derives trigger, portal, and panel anatomy from trigger, title, description, side, align, and showArrow props; manual composition via primitives/popover. Two axes widen it past the click-opened dialog: openOn="hover" reveals the panel on pointer enter after openDelay and hides it after closeDelay, and role="tooltip" makes the panel an inert, non-focusable bubble the Trigger points aria-describedby at.',props:[{name:"trigger",type:"ReactElement | Snippet | TemplateRef<unknown>",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:`React, Svelte, and Angular take this as a prop. Vue and Web Components use named #trigger slot and slot="trigger" instead. Only React merges onto the consumer's element via asChild; others render their own button.`,description:"The trigger for the derived anatomy. Content model varies by framework: React passes a whole element (merged via asChild); Svelte and Angular pass only button inner content."},{name:"title",type:"string",required:!1,description:"The derived panel's accessible name, rendered as the Title part."},{name:"description",type:"string",required:!1,description:"The derived panel's accessible description, rendered as the Description part."},{name:"openOn",type:"'click' | 'hover'",defaultValue:"'click'",required:!1,description:'Which gesture reveals the panel. "click" toggles it from the Trigger button and keeps it open until dismissed. "hover" opens it on pointer enter after openDelay and closes it after closeDelay once the pointer has left both Trigger and Content, so the pointer can travel onto the panel \u2014 the Hoverable requirement of WCAG 2.2 SC 1.4.13. Keyboard focus of the Trigger also opens a hover panel under role="tooltip"; under role="dialog" hover is the only activation there is, so the Trigger must carry an href (or its own tabindex) for keyboard and touch users to reach the same information.'},{name:"role",type:"'dialog' | 'tooltip'",defaultValue:"'dialog'",required:!1,description:'What the panel is. "dialog" is an operable panel: it can be named by Title, hold buttons and links, take focus, and honour modal. "tooltip" is an inert bubble \u2014 Content renders role="tooltip", takes no focus and traps none, Tab passes straight through it, the Trigger points aria-describedby at it while open, Escape and pointer-leave and blur all close it, modal is forced to "none" whatever is passed, and data-state reports delayed-open or instant-open rather than open.'},{name:"side",type:"'top' | 'right' | 'bottom' | 'left' | 'start' | 'end'",defaultValue:"'bottom'",required:!1,description:`Which side of the trigger the derived panel opens on \u2014 forwarded to the underlying Content part. Set Content's own side directly under full manual composition. A tooltip bubble conventionally sits above its trigger, so pass side="top" under role="tooltip".`},{name:"align",type:"'start' | 'center' | 'end'",defaultValue:"'center'",required:!1,description:"Alignment of the derived panel along side \u2014 forwarded to the underlying Content part."},{name:"showArrow",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived panel renders an Arrow part. Gates element creation only; size and position are CSS once created."},{name:"open",type:"boolean",required:!1,description:"Controlled open state. Pair with the open-change event."},{name:"defaultOpen",type:"boolean",defaultValue:"false",required:!1,description:"Initial open state, for uncontrolled usage."},{name:"openDelay",type:"number",defaultValue:"700",required:!1,description:`Delay in milliseconds before a hover popover opens on pointer enter. Read only under openOn="hover"; under role="tooltip" an ancestor Provider's delayDuration applies instead unless this is set, and the Provider's skipDelayDuration can waive the delay entirely for a panel opened just after a sibling closed.`},{name:"closeDelay",type:"number",defaultValue:"300",required:!1,description:`Delay in milliseconds before a hover popover closes once the pointer has left both Trigger and Content. Read only under openOn="hover". Under role="tooltip" an ancestor Provider's closeDelay applies instead unless this is set (0 with no Provider), and while the bubble is hoverable the effective delay is max(closeDelay, 150), protecting the crossing time WCAG 2.2 SC 1.4.13 requires; a bubble with disableHoverableContent uses the value exactly.`},{name:"disableHoverableContent",type:"boolean",defaultValue:"false",required:!1,description:'Whether the pointer moving from Trigger onto Content closes the panel instead of keeping it open. Overrides an ancestor Provider. Defaults to false so the pointer can travel onto the panel, which is the Hoverable requirement of WCAG 2.2 SC 1.4.13; set true only for a bubble that must stay pointer-transparent. Read only under openOn="hover".'},{name:"href",type:"string",required:!1,frameworks:["vue","svelte","angular","webComponents"],exception:"as-directive",frameworksReason:"React's trigger is a whole element supplied through asChild, including its own href, so React declares no member for it; the other four render the anchor themselves and need the destination as a declared property.",description:`Destination for the derived Trigger's anchor. A hover popover under role="dialog" opens on pointer enter and on nothing else, so this link is the only route keyboard and touch users have to the same information; omitting it renders an <a> that is neither in tab order nor a link.`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Whether the panel is suppressed. While true it never opens: the pointer entering Trigger does nothing, aria-describedby is never set, data-state stays closed and open-change never fires. A panel already open when this turns true closes without an event."},{name:"modal",type:"'none' | 'modal' | 'trap-focus'",defaultValue:"'none'",required:!1,description:`How the open popover constrains the page across three independent axes: focus containment, outside-interaction blocking, and page-scroll lock. 'none' (default) applies none of them \u2014 focus stays in the page, the page scrolls, the background stays interactive. 'trap-focus' cycles Tab inside Content and makes the rest of the document inert, leaving page scroll alone. 'modal' applies all three, like Dialog and Drawer, and traps the user. Anything other than 'none' requires a Close part inside Content. Forced to 'none' under role="tooltip", whatever is passed: a bubble that cannot take focus has nothing to contain.`}],events:[{name:"open-change",detail:"boolean",description:"Fired with the new open state whenever it changes, controlled or not."},r("popover"),...n("popover",{outsideNote:'Under role="tooltip" the bubble closes on pointer-leave and on blur rather than on where an outside interaction landed, so the three outside events are notification-only there: the payload stays cancelable, and preventing default changes nothing. escape-key-down is the exception and is listened for on the document while open, so a hover-revealed bubble is dismissable with focus elsewhere (WCAG 2.2 SC 1.4.13).'}),...o("popover")],parts:[{name:"Provider",description:'Optional ancestor coordinating hover-delay timing across every descendant popover with role="tooltip". Renders no element of its own, and a descendant with its own openDelay, closeDelay or disableHoverableContent overrides it. Without one each tooltip falls back to its own values.',props:[{name:"delayDuration",type:"number",defaultValue:"700",required:!1,description:"Delay in milliseconds before a descendant tooltip opens on hover. Each descendant can override it with its own openDelay."},{name:"skipDelayDuration",type:"number",defaultValue:"300",required:!1,description:'Window in milliseconds after a tooltip closes during which sibling tooltips open instantly, so sweeping the pointer along a row of triggers does not re-pay the open delay each time. An instantly-opened bubble reports data-state="instant-open".'},{name:"disableHoverableContent",type:"boolean",defaultValue:"false",required:!1,description:"Whether descendant bubbles ignore the pointer entirely, so moving off Trigger closes them. Defaults to false: the pointer can travel from Trigger into Content, which is what WCAG 2.2 SC 1.4.13 Hoverable requires. Each descendant can override it."},{name:"closeDelay",type:"number",defaultValue:"0",required:!1,description:"Delay in milliseconds before a descendant tooltip closes after the pointer leaves its Trigger. Each descendant can override it, and a hoverable bubble applies the 150 ms floor, so this value can only lengthen the window."}]},{name:"Trigger",description:'The button that opens the popover. Also the default positioning anchor, unless an Anchor part overrides it. Under openOn="hover" it reveals the panel on pointer enter and keeps focus throughout \u2014 the panel never steals it \u2014 and the recipe renders it as an anchor when href is set, which is what makes a hover panel reachable without a pointer. Under role="tooltip" it carries aria-describedby at the open bubble and must hold its own accessible name.',slots:[{name:"default",description:"Trigger's content \u2014 the button (or, with href, the anchor) that reveals the popover and anchors its position."}]},{name:"Anchor",description:"Optional alternative positioning anchor \u2014 Content floats against this element instead of Trigger, for cases where the thing you click and the thing you point at differ."},{...e,description:`${e.description} Framework-conditional: React, Vue, Svelte ship a Portal component. Web Components ships <skdx-popover-portal> as a bare subclass (positioning uses strategy: 'fixed', nothing to escape). Angular uses [skdxPortal] directive. container is reachable everywhere, but in Web Components only as a property \u2014 DOM nodes cannot cross HTML attributes.`},{name:"Content",description:'The floating panel (role="dialog"). Owns positioning, dismissal interceptors (escape-key-down, pointer-down-outside, etc.), focus overrides, and modality (focus trap, inert barrier, scroll lock as modal requests). Under role="tooltip" it renders role="tooltip" instead and is inert: it moves no focus, traps none, holds nothing tabbable, and Tab passes through it as though it were closed \u2014 put buttons and links in a role="dialog" panel. Under openOn="hover" it accepts the pointer by default and reflects data-hoverable while it does, so the pointer can rest on the panel without closing it; disableHoverableContent drops the attribute and ignores the pointer entirely.',props:[...i(),a,{name:"arrowPadding",type:"number",defaultValue:"8",required:!1,description:"Clearance in pixels between the Arrow and panel edges, fed to the floating layer's arrow middleware. Prevents the arrow from sitting over rounded corners when shifted by collision avoidance. Arrow size stays in CSS; only clearance is computed. Default 8 matches the shipped skin's corner radius."},{name:"hideWhenDetached",type:"boolean",defaultValue:"false",required:!1,description:"Hide the panel while its anchor has scrolled out of view inside a clipping ancestor. The panel is hidden (visibility: hidden, pointer-events: none), never unmounted or closed \u2014 unmounting on scroll would tear down the focus trap and move focus unexpectedly. Scrolling the anchor back into view shows it again. Off by default because the extra overflow measurement on every reposition answers a question an anchor outside a scroll container never poses."},{name:"initialFocus",type:"string",required:!1,description:"CSS selector for the element that should receive focus when the popover opens, resolved with querySelector inside Content. Selector form matches Dialog/Drawer. Live only when modal contains focus ('trap-focus' or 'modal'); a 'none' popover never moves focus in. Applies only when open-auto-focus was not prevented. Falls back to the first tabbable element in Content, then Content itself."},{name:"finalFocus",type:"string",required:!1,description:"CSS selector for the element focus should be sent to when the popover closes, resolved at document level. Unlike initialFocus, this is live in every modality: Escape returns focus in all modes. Applies only when close-auto-focus was not prevented. Falls back to the trigger (or whatever was focused before opening) when the selector matches nothing."}]},{name:"Title",description:"The popover's accessible name. Generates an id and wires it onto Content's aria-labelledby while mounted. Optional in the sense that aria-label can replace it, never in the sense the panel may go unnamed.",slots:[{name:"default",description:"Title's content \u2014 the popover's accessible name."}]},{name:"Description",description:"Supporting text for the panel, wired onto Content's aria-describedby the same way and only while mounted.",slots:[{name:"default",description:"Description's content \u2014 the popover's accessible description."}]},t,{name:"Close",description:"A button inside Content that closes the popover."}],slots:[{name:"trigger",description:"Trigger's content \u2014 the button (or, with href, the anchor) that reveals the popover and anchors its position."},{name:"default",description:"The panel's body content, including a tooltip bubble's label text. In manual composition, Content's body from Title, Description, Arrow, and Close."}],states:["closed","open","delayed-open","instant-open"],controlledUncontrolled:"Stateless by default (uncontrolled): starts at defaultOpen and manages its own state internally. Passing open switches to controlled.",formBehavior:'Not applicable \u2014 Popover participates in no form; Trigger and Close are type="button".',validationBehavior:"Not applicable.",accessibility:{role:'dialog, or tooltip under role="tooltip"',keyboard:[{key:"Escape",behavior:'Requests close (cancelable via escape-key-down), refocuses Trigger. Under role="tooltip" the listener is on the document, so a hover-revealed bubble is dismissable even when focus is elsewhere (WCAG 2.2 SC 1.4.13).'},{key:"Tab / Shift+Tab",behavior:`Cycles focus within Content only while modal contains focus ('trap-focus' or 'modal'); leaves the panel into the page when modal is 'none'. Under role="tooltip" it is never intercepted \u2014 the bubble holds nothing tabbable and Tab moves through the page as if it were closed.`}],ariaAttributes:['aria-haspopup="dialog", aria-expanded, aria-controls on Trigger',"Trigger's aria-controls points at Content's id","aria-labelledby on Content points at Title's id, aria-describedby at Description's \u2014 each only while mounted","While focus-containing ('trap-focus' or true), everything outside is inert; focus is trapped","Focus-containing popovers must render a Close part \u2014 Escape is unavailable to touch screen-reader users",`Under role="tooltip" the Trigger instead points aria-describedby at Content's id, only while open, and neither aria-haspopup nor aria-expanded is emitted. Content must then hold text only: aria-describedby flattens every role and boundary inside it, so rich or interactive content belongs in a role="dialog" panel.`,"A hover popover accepts the pointer by default: it may leave Trigger and rest on the panel without it closing, which is the Hoverable requirement of WCAG 2.2 SC 1.4.13.","A hover popover is unreachable on touch-only input \u2014 there is no long-press path, and platform gestures take precedence. Anything it conveys must also be available from a control touch can reach, which is what the Trigger's href is for.","A tooltip bubble must contain no Trigger of its own. Nesting is unsupported: Escape is claimed by the top dismiss layer only, so an inner panel would swallow the outer one's dismissal."],focusManagement:`Traps focus in Content only when modal contains it ('trap-focus' or 'modal'); always returns focus to Trigger on Escape. Announces both moves as cancelable open-auto-focus / close-auto-focus events. Content's initialFocus and finalFocus selectors redirect these moves (applied only when events not prevented). initialFocus is inert while modal is 'none'; finalFocus is live in every mode. Under role="tooltip" the panel moves no focus at all in either direction \u2014 Trigger keeps it and the bubble only appears and disappears around it \u2014 so initialFocus goes unread and modal is forced to 'none'. A hover panel under role="dialog" likewise never pulls the caret off the trigger; if its content has to be operated, open it on click instead.`,namingRequirement:'Content is a role="dialog" and must be named: render a Title or set aria-label on Content. Unnamed panels announce as bare "dialog". Under role="tooltip" the obligation moves to the Trigger, which must carry its own accessible name \u2014 visible text, or an aria-label when icon-only. The bubble is wired as a description, and a description is not a name: an icon button whose only text lives in its tooltip is an unnamed button, and Content is never a naming source. A hover panel needs a focusable Trigger for the same reason \u2014 an <a> with no href is neither in tab order nor a link.'},styling:{cssParts:["trigger","anchor","content","title","description","arrow","close"],dataAttributes:[...l,"data-state","data-side","data-align","data-hoverable",{name:"data-skdx-popover-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:"Marks a node the Web Components port generated for itself rather than one the consumer authored; the other four frameworks re-render their derived anatomy declaratively, so they never need this disambiguation."}],cssCustomProperties:s("popover")},publicMethods:[{name:"reposition",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React's root forwards no imperative handle and renders no element, so there is no instance to hang a method off \u2014 usePopoverContext is React's binding of this member and it is declared under contextApi.",signature:"() => void",description:"Recomputes the open panel's position immediately. Needed when the anchor moves for a reason the DOM never reports (CSS transform finishing, virtual list reflow, etc.). No-op while closed or unmounted."}],contextApi:[{name:"reposition",signature:"() => void",description:"Recomputes the open panel's position immediately for an anchor that moved without observers noticing.",accessors:{react:"usePopoverContext('MyComponent').reposition",vue:"popoverRef.value.reposition() \u2014 defineExpose'd on SkdxPopover",svelte:"popover.reposition() \u2014 the instance export on Popover",angular:"skdxPopoverRef.reposition()",webComponents:"document.querySelector('skdx-popover').reposition()"}}]};export{c as popoverContract};
@@ -1 +1 @@
1
- import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var t={name:"Progress",purpose:'A bar for a number inside a known range. The root clamps value between min and max, renders role="progressbar" with the matching aria-value* attributes, and shares value/min/max with an Indicator part via context so the fill can be styled purely from CSS. With role="meter" the same bar becomes a gauge for a measurement that is not going anywhere \u2014 disk usage, a score, a battery \u2014 where low/high/optimum sort the range into bands published as data-range.',props:[{name:"role",type:"'progressbar' | 'meter'",defaultValue:"'progressbar'",required:!1,description:'What the bar is reporting, and therefore the ARIA role the root renders. "progressbar" is a task advancing towards completion and may be indeterminate or buffered. "meter" is a static measurement inside a range: aria-valuenow is always present, low/high/optimum sort the range into bands, and indeterminate and buffer have no meaning there.'},{name:"value",type:"number | null",defaultValue:"null",required:!1,description:'Current value. null renders an indeterminate progress bar; under role="meter" it reads as min instead, because a meter always reports a number and role="meter" requires aria-valuenow.'},{name:"min",type:"number",defaultValue:"0",required:!1,description:"Lower bound of the range, reflected as aria-valuemin. Defaults to 0, the value the range started at before the prop existed, so an existing bar is unaffected."},{name:"max",type:"number",defaultValue:"100",required:!1,description:"Upper bound of the range that value is measured against. Raised to min if passed below it. Defaults to 100 rather than the HTML <meter> element's 1, so the default percentage label reads as the literal value."},{name:"low",type:"number",required:!1,description:'Upper edge of the low band. Together with high and optimum it sorts the range into optimum/suboptimal/poor regions, published as data-range. Region rules follow the HTML <meter> element. Applies only under role="meter".'},{name:"high",type:"number",required:!1,description:'Lower edge of the high band, the counterpart of low and read under the same rules. Applies only under role="meter".'},{name:"optimum",type:"number",required:!1,description:'The ideal value, whose position relative to low and high decides which end of the range is good: below low makes low values good (disk usage), above high makes high values good (battery), between them makes the middle band good and leaves no poor band. Defaults to the range midpoint when low or high is given without it. Applies only under role="meter".'},{name:"buffer",type:"number | null",defaultValue:"null",required:!1,description:'A second value clamped into [min, max] and published as --skdx-progress-buffer. aria-valuenow keeps reporting value, since a buffer is not progress the user made. Applies only under role="progressbar": a measurement has nothing loading behind it.'},{name:"formatOptions",type:"Intl.NumberFormatOptions",required:!1,description:'Intl.NumberFormat options the value is formatted with for aria-valuetext and the Value part (currency, percent, unit, grouping) \u2014 the canonical formatting route. Formats the raw value, exactly as NumberField does, so with style: percent the value is the fraction and 0.5 reads "50%". Serializable, so it crosses the Web Components attribute boundary as JSON where a function cannot; getValueLabel remains the escape hatch for a string Intl cannot produce, and wins when both are supplied.'},{name:"locale",type:"string",required:!1,description:"BCP-47 locale the formatting uses, resolved once in the root through Intl.NumberFormat(locale).resolvedOptions().locale so a server render and a browser render agree. Defaults to the runtime locale. Resolution only happens while formatting is on (locale or formatOptions given); without either, the label falls through to the default percentage untouched by Intl."},{name:"getValueLabel",type:"(value: number, min: number, max: number) => string",required:!1,description:'Formats aria-valuetext. Defaults to a rounded percentage of the position within [min, max], e.g. "66%". Takes min as well as max because the range does not have to start at zero. Supplying it is the escape hatch: formatOptions is the canonical route and covers currency, percent, unit and grouping, so reach for this only when the string is one Intl cannot produce. It wins over formatOptions when both are given.'},{name:"circular",type:"boolean",defaultValue:"false",required:!1,description:"Renders the circular anatomy \u2014 a ring and its indicator \u2014 instead of the linear bar."},{name:"label",type:"string",required:!1,description:"Text naming what is progressing or being measured; when absent no label element is rendered. Renders in both the linear and the circular shape, under either role."},{name:"showValue",type:"boolean",defaultValue:"false",required:!1,description:"Renders the formatted value alongside the indicator."},{name:"indeterminate",type:"boolean",defaultValue:"false",required:!1,description:'Marks progress as unmeasurable, overriding `value`. Equivalent to `value: null`, which stays supported; when both are given `indeterminate` wins. Applies only under role="progressbar" \u2014 a meter always reports a number, so it is ignored there.'},{name:"diameter",type:"number",defaultValue:"48",required:!1,description:"Outer diameter of the circular track, in the unitless SVG user units the Circle part documents \u2014 the same number the consumer puts in their own viewBox, not a CSS length. Applies only when `circular`."},{name:"strokeWidth",type:"number",defaultValue:"4",required:!1,description:"Stroke width of the circular track, in the same unitless units as `diameter`. Insets the radius by half its value so the stroke stays inside the diameter box. Applies only when `circular`."}],events:[],parts:[{name:"Label",description:`The bar's visible name. Generates an id the root points aria-labelledby at while this part is mounted, so a bar with a visible caption needs no second copy of that caption in an aria-label. Neither role="progressbar" nor role="meter" takes a name from its contents, which is exactly why the id has to be wired rather than left to the accessible-name computation.`},{name:"Value",description:"The visible readout \u2014 the same string the root announces through aria-valuetext. Rendered aria-hidden for that reason: without it a screen reader hears the value twice, once as the root's valuetext and once as descendant text. Renders getValueLabel(value, min, max) when given no children of its own, and nothing at all while indeterminate, because there is no value to read out."},{name:"Indicator",description:`The visual fill inside the linear root. Mirrors the root's data-state / data-value / data-min / data-max \u2014 and data-range under role="meter", so a skin can paint the band without reading the thresholds itself \u2014 and sets --skdx-progress to the position of value inside [min, max], so width or transform can be driven purely from CSS. Publishes --skdx-progress-buffer the same way when a buffer is set. Both properties are absent while indeterminate (and the buffer one when no buffer was given), so an unset value falls through to the consumer's own animation instead of pinning the fill at a value the component does not have.`},{name:"Circle",description:`Geometry host for a ring. Renders no SVG of its own \u2014 it publishes the ring's arithmetic as CSS custom properties (--skdx-progress-circle-size, --skdx-progress-circle-thickness, --skdx-progress-circle-radius, --skdx-progress-circle-circumference, --skdx-progress-dasharray, --skdx-progress-circle-offset) so the consumer brings their own <svg> and drives stroke-dasharray / stroke-dashoffset from CSS alone. The first two keep their published names after the diameter/strokeWidth prop rename: a renamed prop breaks at compile time, a renamed custom property breaks silently in a stylesheet no compiler reads. Mirrors the root's data-state / data-value / data-min / data-max, and adds no second role="progressbar": the ARIA lives on the root this must be used inside.`,props:[{name:"diameter",type:"number",defaultValue:"48",required:!1,description:"Outer diameter of the ring in unitless SVG user units \u2014 the number that also goes in the consumer's own viewBox. Unitless rather than a CSS length because it feeds stroke-dasharray/stroke-dashoffset, which are path lengths CSS cannot convert. Named diameter rather than size: the system reserves size for the appearance rung no component ships, and a geometry number that reads as a design token is a number some tool will eventually mis-read."},{name:"strokeWidth",type:"number",defaultValue:"4",required:!1,description:"Stroke width of the ring in the same unitless units as diameter. Insets the radius by half its value ((diameter - strokeWidth) / 2) so the stroke stays inside the diameter box rather than clipping at the viewBox edge. Named after the SVG presentation attribute it feeds, following the diameter rename."}]},{name:"CircleIndicator",description:"The swept arc of a Circle \u2014 the circular counterpart of Indicator, usually rendered onto the consumer's own <circle> or <path> because the default element has no stroke. Mirrors the root's data-state / data-value / data-max, repeats the value-dependent ring properties (--skdx-progress-circle-circumference, --skdx-progress-dasharray, --skdx-progress-circle-offset) on itself so the arc element can be styled directly, and adds --skdx-progress as a percentage for anything measured in percent rather than path length. Must be used inside a Circle."}],slots:[{name:"default",description:"The bar's body \u2014 typically an Indicator part, or a Circle wrapping the consumer's own <svg> with a CircleIndicator on the arc, optionally preceded by a Label and a Value."}],states:["indeterminate","loading","complete","optimum","suboptimal","poor"],controlledUncontrolled:"Stateless \u2014 the root only clamps and mirrors the value prop, it holds no state.",formBehavior:"Not applicable \u2014 the bar participates in no form.",validationBehavior:"Not applicable \u2014 the bar carries a value but participates in no validation.",accessibility:{role:'progressbar (default), or meter when role="meter"',keyboard:[],ariaAttributes:["aria-valuemin reflects the min prop (0 by default)","aria-valuemax reflects the max prop",'aria-valuenow reflects the clamped value \u2014 omitted when indeterminate, and always present under role="meter", which has no indeterminate state','aria-valuetext reflects getValueLabel(value, min, max), or Intl.NumberFormat(locale, formatOptions).format(value) when formatOptions or locale is supplied instead (omitted when indeterminate). Under role="meter" it is emitted only when one of those is supplied or the range is 0..100, because a rounded percentage is not the value on any other range and it overrides the aria-valuenow that is',"aria-labelledby points at the Label part while one is mounted, unless the consumer supplied their own"],focusManagement:"Not focusable; not part of the tab order.",namingRequirement:"The root must carry an accessible name via aria-label, a resolving aria-labelledby, or a mounted Label part (which wires aria-labelledby for the consumer). Neither role takes a name from its contents, so the percentage rendered inside the bar is announced as a bare number with nothing saying what is at that number."},styling:{cssParts:["root","label","value","indicator","circle","circle-indicator"],dataAttributes:[...e,"data-state","data-value","data-min","data-max",{name:"data-range",description:'On the root and the Indicator under role="meter": which band the value falls into \u2014 optimum, suboptimal or poor \u2014 derived from low/high/optimum, and absent when no threshold was given.'},{name:"data-skdx-progress-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-progress","--skdx-progress-buffer","--skdx-progress-dasharray","--skdx-progress-circle-size","--skdx-progress-circle-thickness","--skdx-progress-circle-radius","--skdx-progress-circle-circumference","--skdx-progress-circle-offset"]},publicMethods:[]};export{t as progressContract};
1
+ import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var r={name:"Progress",purpose:'A bar for a number inside a known range. The root clamps value between min and max, renders role="progressbar" with the matching aria-value* attributes, and shares value/min/max with an Indicator part via context so the fill can be styled purely from CSS. With role="meter" the same bar becomes a gauge for a measurement that is not going anywhere \u2014 disk usage, a score, a battery \u2014 where low/high/optimum sort the range into bands published as data-range.',props:[{name:"role",type:"'progressbar' | 'meter'",defaultValue:"'progressbar'",required:!1,description:'What the bar is reporting, and therefore the ARIA role the root renders. "progressbar" is a task advancing towards completion and may be indeterminate or buffered. "meter" is a static measurement inside a range: aria-valuenow is always present, low/high/optimum sort the range into bands, and indeterminate and buffer have no meaning there.'},{name:"value",type:"number | null",defaultValue:"null",required:!1,description:'Current value. null renders an indeterminate progress bar; under role="meter" it reads as min instead, because a meter always reports a number and role="meter" requires aria-valuenow.'},{name:"min",type:"number",defaultValue:"0",required:!1,description:"Lower bound of the range, reflected as aria-valuemin. Defaults to 0, the value the range started at before the prop existed, so an existing bar is unaffected."},{name:"max",type:"number",defaultValue:"100",required:!1,description:"Upper bound of the range that value is measured against. Raised to min if passed below it. Defaults to 100 rather than the HTML <meter> element's 1, so the default percentage label reads as the literal value."},{name:"low",type:"number",required:!1,description:'Upper edge of the low band. Together with high and optimum it sorts the range into optimum/suboptimal/poor regions, published as data-range. Region rules follow the HTML <meter> element. Applies only under role="meter".'},{name:"high",type:"number",required:!1,description:'Lower edge of the high band, the counterpart of low and read under the same rules. Applies only under role="meter".'},{name:"optimum",type:"number",required:!1,description:'The ideal value, whose position relative to low and high decides which end of the range is good: below low makes low values good (disk usage), above high makes high values good (battery), between them makes the middle band good and leaves no poor band. Defaults to the range midpoint when low or high is given without it. Applies only under role="meter".'},{name:"buffer",type:"number | null",defaultValue:"null",required:!1,description:'A second value clamped into [min, max] and published as --skdx-progress-buffer. aria-valuenow keeps reporting value, since a buffer is not progress the user made. Applies only under role="progressbar": a measurement has nothing loading behind it.'},{name:"formatOptions",type:"Intl.NumberFormatOptions",required:!1,description:'Intl.NumberFormat options the value is formatted with for aria-valuetext and the Value part (currency, percent, unit, grouping) \u2014 the canonical formatting route. Formats the raw value, exactly as NumberField does, so with style: percent the value is the fraction and 0.5 reads "50%". Serializable, so it crosses the Web Components attribute boundary as JSON where a function cannot; getValueLabel remains the escape hatch for a string Intl cannot produce, and wins when both are supplied.'},{name:"locale",type:"string",required:!1,description:"BCP-47 locale the formatting uses, resolved once in the root through Intl.NumberFormat(locale).resolvedOptions().locale so a server render and a browser render agree. Defaults to the runtime locale. Resolution only happens while formatting is on (locale or formatOptions given); without either, the label falls through to the default percentage untouched by Intl."},{name:"getValueLabel",type:"(value: number, min: number, max: number) => string",required:!1,description:'Formats aria-valuetext. Defaults to a rounded percentage of the position within [min, max], e.g. "66%". Takes min as well as max because the range does not have to start at zero. Supplying it is the escape hatch: formatOptions is the canonical route and covers currency, percent, unit and grouping, so reach for this only when the string is one Intl cannot produce. It wins over formatOptions when both are given.'},{name:"circular",type:"boolean",defaultValue:"false",required:!1,description:"Renders the circular anatomy \u2014 a ring and its indicator \u2014 instead of the linear bar."},{name:"label",type:"string",required:!1,description:"Text naming what is progressing or being measured; when absent no label element is rendered. Renders in both the linear and the circular shape, under either role."},{name:"showValue",type:"boolean",defaultValue:"false",required:!1,description:"Renders the formatted value alongside the indicator."},{name:"indeterminate",type:"boolean",defaultValue:"false",required:!1,description:'Marks progress as unmeasurable, overriding `value`. Equivalent to `value: null`, which stays supported; when both are given `indeterminate` wins. Applies only under role="progressbar" \u2014 a meter always reports a number, so it is ignored there.'},{name:"diameter",type:"number",defaultValue:"48",required:!1,description:"Outer diameter of the circular track, in the unitless SVG user units the Circle part documents \u2014 the same number the consumer puts in their own viewBox, not a CSS length. Applies only when `circular`."},{name:"strokeWidth",type:"number",defaultValue:"4",required:!1,description:"Stroke width of the circular track, in the same unitless units as `diameter`. Insets the radius by half its value so the stroke stays inside the diameter box. Applies only when `circular`."}],events:[],parts:[{name:"Label",description:`The bar's visible name. Generates an id the root points aria-labelledby at while this part is mounted, so a bar with a visible caption needs no second copy of that caption in an aria-label. Neither role="progressbar" nor role="meter" takes a name from its contents, which is exactly why the id has to be wired rather than left to the accessible-name computation.`},{name:"Value",description:"The visible readout \u2014 the same string the root announces through aria-valuetext. Rendered aria-hidden for that reason: without it a screen reader hears the value twice, once as the root's valuetext and once as descendant text. Renders getValueLabel(value, min, max) when given no children of its own, and nothing at all while indeterminate, because there is no value to read out."},{name:"Indicator",description:`The visual fill inside the linear root. Mirrors the root's data-state / data-value / data-min / data-max \u2014 and data-range under role="meter", so a skin can paint the band without reading the thresholds itself \u2014 and sets --skdx-progress to the position of value inside [min, max], so width or transform can be driven purely from CSS. Publishes --skdx-progress-buffer the same way when a buffer is set. Both properties are absent while indeterminate (and the buffer one when no buffer was given), so an unset value falls through to the consumer's own animation instead of pinning the fill at a value the component does not have.`},{name:"Circle",description:`Geometry host for a ring. Renders no SVG of its own \u2014 it publishes the ring's arithmetic as CSS custom properties (--skdx-progress-circle-size, --skdx-progress-circle-thickness, --skdx-progress-circle-radius, --skdx-progress-circle-circumference, --skdx-progress-dasharray, --skdx-progress-circle-offset) so the consumer brings their own <svg> and drives stroke-dasharray / stroke-dashoffset from CSS alone. The first two keep their published names after the diameter/strokeWidth prop rename: a renamed prop breaks at compile time, a renamed custom property breaks silently in a stylesheet no compiler reads. Mirrors the root's data-state / data-value / data-min / data-max, and adds no second role="progressbar": the ARIA lives on the root this must be used inside.`,props:[{name:"diameter",type:"number",defaultValue:"48",required:!1,description:"Outer diameter of the ring in unitless SVG user units \u2014 the number that also goes in the consumer's own viewBox. Unitless rather than a CSS length because it feeds stroke-dasharray/stroke-dashoffset, which are path lengths CSS cannot convert. Named diameter rather than size: the system reserves size for the appearance rung no component ships, and a geometry number that reads as a design token is a number some tool will eventually mis-read."},{name:"strokeWidth",type:"number",defaultValue:"4",required:!1,description:"Stroke width of the ring in the same unitless units as diameter. Insets the radius by half its value ((diameter - strokeWidth) / 2) so the stroke stays inside the diameter box rather than clipping at the viewBox edge. Named after the SVG presentation attribute it feeds, following the diameter rename."}]},{name:"CircleIndicator",description:"The swept arc of a Circle \u2014 the circular counterpart of Indicator, usually rendered onto the consumer's own <circle> or <path> because the default element has no stroke. Mirrors the root's data-state / data-value / data-max, repeats the value-dependent ring properties (--skdx-progress-circle-circumference, --skdx-progress-dasharray, --skdx-progress-circle-offset) on itself so the arc element can be styled directly, and adds --skdx-progress as a percentage for anything measured in percent rather than path length. Must be used inside a Circle."}],slots:[{name:"default",description:"The bar's body \u2014 typically an Indicator part, or a Circle wrapping the consumer's own <svg> with a CircleIndicator on the arc, optionally preceded by a Label and a Value."}],states:["indeterminate","loading","complete","optimum","suboptimal","poor"],controlledUncontrolled:"Stateless \u2014 the root only clamps and mirrors the value prop, it holds no state.",formBehavior:"Not applicable \u2014 the bar participates in no form.",validationBehavior:"Not applicable \u2014 the bar carries a value but participates in no validation.",accessibility:{role:'progressbar (default), or meter when role="meter"',keyboard:[],ariaAttributes:["aria-valuemin reflects the min prop (0 by default)","aria-valuemax reflects the max prop",'aria-valuenow reflects the clamped value \u2014 omitted when indeterminate, and always present under role="meter", which has no indeterminate state','aria-valuetext reflects getValueLabel(value, min, max), or Intl.NumberFormat(locale, formatOptions).format(value) when formatOptions or locale is supplied instead (omitted when indeterminate). Under role="meter" it is emitted only when one of those is supplied or the range is 0..100, because a rounded percentage is not the value on any other range and it overrides the aria-valuenow that is',"aria-labelledby points at the Label part while one is mounted, unless the consumer supplied their own"],focusManagement:"Not focusable; not part of the tab order.",namingRequirement:"The root must carry an accessible name via aria-label, a resolving aria-labelledby, or a mounted Label part (which wires aria-labelledby for the consumer). Neither role takes a name from its contents, so the percentage rendered inside the bar is announced as a bare number with nothing saying what is at that number."},styling:{cssParts:["root","label","value","indicator","circle","circle-indicator"],dataAttributes:[...e,"data-state","data-value","data-min","data-max",{name:"data-range",description:'On the root and the Indicator under role="meter": which band the value falls into \u2014 optimum, suboptimal or poor \u2014 derived from low/high/optimum, and absent when no threshold was given.'},{name:"data-skdx-progress-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-progress","--skdx-progress-buffer","--skdx-progress-dasharray","--skdx-progress-circle-size","--skdx-progress-circle-thickness","--skdx-progress-circle-radius","--skdx-progress-circle-circumference","--skdx-progress-circle-offset"]},publicMethods:[],frameworkExtensions:[{framework:"webComponents",part:"Circle",name:"circumference",type:"number",description:"Circumference of the ring's centre line, `2\u03C0r` \u2014 the full `stroke-dasharray` length. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings.",kind:"prop"},{framework:"webComponents",part:"Circle",name:"radius",type:"number",description:"Radius of the ring's centre line, inset by `strokeWidth / 2` and floored at `0`. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings.",kind:"prop"},{framework:"webComponents",part:"Circle",name:"circumference",type:"number",description:"Circumference of the ring's centre line, `2\u03C0r` \u2014 the full `stroke-dasharray` length. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings."},{framework:"webComponents",part:"Circle",name:"radius",type:"number",description:"Radius of the ring's centre line, inset by `strokeWidth / 2` and floored at `0`. This documented getter exposes the native element\u2019s derived live state. Other wrappers expose their state through context or framework refs; the shared component contract does not require identical getter spellings."}]};export{r as progressContract};
@@ -1 +1 @@
1
- import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var r={name:"QrCode",purpose:"A QR code encoder. The root encodes a string into a module matrix, shares it with an SVG Frame and Pattern path, and optional Overlay for a logo. Payload rendered as visually hidden text \u2014 not a dead end without a camera.",props:[{name:"value",type:"string",required:!0,description:"The payload to encode. Encoded as UTF-8 bytes; only length can exceed capacity."},{name:"errorCorrection",type:"'L' | 'M' | 'Q' | 'H'",defaultValue:"'M'",required:!1,description:"Error-correction level: L ~7%, M 15%, Q 25%, H 30% redundancy. Overlay requires H \u2014 nothing raises it automatically."},{name:"version",type:"number",required:!1,description:"Force a symbol version (1\u201340), fixing the module count at 4 \xD7 version + 17. Too small for the value is an encode failure, not a silent upgrade."},{name:"hiddenLabel",type:"boolean | string",defaultValue:"true",required:!1,description:"Visually hidden text inside the root, so a screen-reader user reaches the destination without a camera. true renders value; string renders that instead; false renders none. Pass string for payloads not meant to be read aloud (credentials, tokens)."},{name:"overlay",type:"unknown",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:"Vue and Web Components use a named slot \u2014 Vue idiom, Web Components because attributes carry strings, not nodes. Both slot spellings declared under `slots`.",description:"Content laid over the centre of the code, typically a logo. The code's error correction level must be high enough to survive the occlusion."}],events:[{name:"encode-error",description:"Fired when value cannot be encoded \u2014 exceeds capacity or version is outside 1\u201340. The root renders nothing in that case rather than a broken symbol.",detail:"Error"}],parts:[{name:"Frame",description:'The <svg> host with viewBox extent as module count. ARIA follows what it is named \u2014 role="img" if labelled, aria-hidden if not. Use aria-label or aria-labelledby; <title> children do not count.'},{name:"Pattern",description:"The single <path> carrying every dark module. One path rather than one rect per module \u2014 for page weight."},{name:"Overlay",description:"The centred slot for a logo, drawn in module units so it scales with the symbol. Warns in development when errorCorrection is not 'H' \u2014 overlay destroys modules that only 'H' can reconstruct.",props:[{name:"extent",type:"number",defaultValue:"7",required:!1,description:"Edge length of the overlay box in module units (same coordinate space as viewBox). Defaults to finder pattern width. Keep it under roughly a fifth of the symbol's edge \u2014 larger patches damage more modules than H error correction can reconstruct."}]}],slots:[{name:"default",description:"The code's body \u2014 a Frame wrapping a Pattern, and optionally an Overlay."},{name:"overlay",description:"Overlay's content \u2014 typically a logo drawn over the centre of the code."}],states:[],controlledUncontrolled:"Stateless \u2014 the matrix is derived from value, errorCorrection and version on every change, and nothing is held between renders.",formBehavior:"Not applicable \u2014 QrCode renders a value, it collects none.",validationBehavior:"Encoding capacity is the only constraint, and exceeding it is reported through the encode-error event rather than rendered as an invalid state.",accessibility:{role:"img (Frame, only when named \u2014 aria-hidden otherwise)",keyboard:[],ariaAttributes:['role="img" on the Frame when named; aria-hidden on the Frame when no name is given',"Visually hidden payload text (rendered on root by default) gives non-sighted users the destination"],focusManagement:"None \u2014 nothing here is focusable. A QR code is content, not a control; if it should be actionable, the consumer wraps it in a link or a button.",namingRequirement:"Name the Frame when the symbol is the only copy of the destination. Leave unnamed when the destination is written out beside it."},styling:{cssParts:["root","frame","svg","pattern","overlay","hidden-label"],dataAttributes:[...e,{name:"data-skdx-qr-code-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-qr-code-size"]},publicMethods:[]};export{r as qrCodeContract};
1
+ import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var r={name:"QrCode",purpose:"A QR code encoder. The root encodes a string into a module matrix, shares it with an SVG Frame and Pattern path, and optional Overlay for a logo. Payload rendered as visually hidden text \u2014 not a dead end without a camera.",props:[{name:"value",type:"string",required:!0,description:"The payload to encode. Encoded as UTF-8 bytes; only length can exceed capacity."},{name:"errorCorrection",type:"'L' | 'M' | 'Q' | 'H'",defaultValue:"'M'",required:!1,description:"Error-correction level: L ~7%, M 15%, Q 25%, H 30% redundancy. Overlay requires H \u2014 nothing raises it automatically."},{name:"version",type:"number",required:!1,description:"Force a symbol version (1\u201340), fixing the module count at 4 \xD7 version + 17. Too small for the value is an encode failure, not a silent upgrade."},{name:"hiddenLabel",type:"boolean | string",defaultValue:"true",required:!1,description:"Visually hidden text inside the root, so a screen-reader user reaches the destination without a camera. true renders value; string renders that instead; false renders none. Pass string for payloads not meant to be read aloud (credentials, tokens)."},{name:"overlay",type:"unknown",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:"Vue and Web Components use a named slot \u2014 Vue idiom, Web Components because attributes carry strings, not nodes. Both slot spellings declared under `slots`.",description:"Content laid over the centre of the code, typically a logo. The code's error correction level must be high enough to survive the occlusion."}],events:[{name:"encode-error",description:"Fired when value cannot be encoded \u2014 exceeds capacity or version is outside 1\u201340. The root renders nothing in that case rather than a broken symbol.",detail:"Error"}],parts:[{name:"Frame",description:'The <svg> host with viewBox extent as module count. ARIA follows what it is named \u2014 role="img" if labelled, aria-hidden if not. Use aria-label or aria-labelledby; <title> children do not count.'},{name:"Pattern",description:"The single <path> carrying every dark module. One path rather than one rect per module \u2014 for page weight."},{name:"Overlay",description:"The centred slot for a logo, drawn in module units so it scales with the symbol. Warns in development when errorCorrection is not 'H' \u2014 overlay destroys modules that only 'H' can reconstruct.",props:[{name:"extent",type:"number",defaultValue:"7",required:!1,description:"Edge length of the overlay box in module units (same coordinate space as viewBox). Defaults to finder pattern width. Keep it under roughly a fifth of the symbol's edge \u2014 larger patches damage more modules than H error correction can reconstruct."}]}],slots:[{name:"default",description:"The code's body \u2014 a Frame wrapping a Pattern, and optionally an Overlay."},{name:"overlay",description:"Overlay's content \u2014 typically a logo drawn over the centre of the code."}],states:[],controlledUncontrolled:"Stateless \u2014 the matrix is derived from value, errorCorrection and version on every change, and nothing is held between renders.",formBehavior:"Not applicable \u2014 QrCode renders a value, it collects none.",validationBehavior:"Encoding capacity is the only constraint, and exceeding it is reported through the encode-error event rather than rendered as an invalid state.",accessibility:{role:"img (Frame, only when named \u2014 aria-hidden otherwise)",keyboard:[],ariaAttributes:['role="img" on the Frame when named; aria-hidden on the Frame when no name is given',"Visually hidden payload text (rendered on root by default) gives non-sighted users the destination"],focusManagement:"None \u2014 nothing here is focusable. A QR code is content, not a control; if it should be actionable, the consumer wraps it in a link or a button.",namingRequirement:"Name the Frame when the symbol is the only copy of the destination. Leave unnamed when the destination is written out beside it."},styling:{cssParts:["root","frame","svg","pattern","overlay","hidden-label"],dataAttributes:[...e,{name:"data-skdx-qr-code-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-qr-code-size"]},publicMethods:[],frameworkExtensions:[{framework:"webComponents",part:"Pattern",name:"fill",type:"string",description:"Paint for the dark modules, forwarded to the `<path>`. This is documented configuration/state on the native element or its composed part beyond the common component core. It is explicitly recorded as an element adapter rather than falsely requiring the same property binding on every wrapper.",kind:"prop"},{framework:"webComponents",part:"Pattern",name:"fill",type:"string",description:"Paint for the dark modules, forwarded to the `<path>`. This is documented configuration/state on the native element or its composed part beyond the common component core. It is explicitly recorded as an element adapter rather than falsely requiring the same property binding on every wrapper."}]};export{r as qrCodeContract};
@@ -1 +1 @@
1
- import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"RadioCard",purpose:'A radio with card-sized hit target for RadioGroup, built as a <label> wrapping a hidden <input type="radio">, with Indicator, Label, and Description parts.',props:[{name:"value",type:"string",required:!0,description:"Unique identifier compared against the RadioGroup's selected value."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables this card only; a group-level disabled takes precedence and cannot be re-enabled per card."},{name:"hasLabel",type:"boolean",defaultValue:"false",required:!1,description:"Svelte-only: declares that a Label is present, so server-rendered markup carries aria-labelledby before hydration. Other frameworks detect it during render.",frameworks:["svelte"],exception:"snippet-opaque",frameworksReason:"React and Vue scan children for the part during render; Angular detects it in its first change-detection pass; Web Components queries real DOM children. Svelte receives children as an opaque Snippet, so it takes an explicit prop instead. The real registration on mount is authoritative."},{name:"hasDescription",type:"boolean",defaultValue:"false",required:!1,description:"Svelte-only: declares a Description is present for aria-describedby.",frameworks:["svelte"],exception:"snippet-opaque",frameworksReason:"Same reason as hasLabel: Svelte cannot inspect a Snippet, so it needs an explicit prop."},{name:"label",type:"string",required:!1,description:"Title text, rendered into the Label that `aria-labelledby` points at. When supplied, the card derives Control, Indicator, and Label automatically; omit it and an `aria-label` on the control is required. This all-or-nothing behaviour preserves the anatomy as the card itself, not optional additions."},{name:"description",type:"string",required:!1,description:"Supporting copy under the title, which derives a Description part and `aria-describedby`. The only optional part of the derived anatomy; a card without supporting copy is still a card."},{name:"checked",type:"boolean",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Only the custom element publishes it. The other four hand this derived state down to their parts through context, so a consumer never reads it off a part; here every part is its own element with no such channel, and reading it back is the only way to reflect the state in surrounding markup.",description:"Whether this card currently holds the group's selected value. Read-only: pick it through the group's value. It has no defaultChecked twin, because the seed for an uncontrolled group is RadioGroup's own defaultValue and a second seed on the card would let the two disagree."},{name:"effectiveDisabled",type:"boolean",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Only the custom element publishes it. The other four hand this derived state down to their parts through context, so a consumer never reads it off a part; here every part is its own element with no such channel, and reading it back is the only way to reflect the state in surrounding markup.",description:"Whether this card is disabled, by its own disabled or by the group's. Read-only: it is the OR of the two."},{name:"size",type:"'sm' | 'md' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[],parts:[{name:"Control",description:"The hidden radio input, still focusable so the browser owns activation and focus. Points aria-labelledby at the Label and aria-describedby at the Description when mounted."},{name:"Indicator",description:"The drawn dot or ring, aria-hidden because the control already reports its own checked state."},{name:"Label",description:"The card's title, and the accessible name."},{name:"Description",description:"Supporting copy announced after the name, referenced by aria-describedby so it is pronounced rather than folded into it."}],slots:[{name:"default",description:"The card's body: Control, Indicator, Label, and usually Description."}],appearance:[{attribute:"data-size",values:["sm","md","lg"],stylesheet:"radio-card.css"}],states:["checked","unchecked","focus-visible","disabled"],controlledUncontrolled:"Neither \u2014 RadioCard holds no state of its own. Selection belongs to the enclosing RadioGroup.",formBehavior:`A real <input type="radio"> carrying the group's name in React, Vue, Svelte and Angular, so exclusivity and form submission are native rather than reimplemented. The Web Components Control is an ARIA radio instead \u2014 a visually-hidden element with role="radio", aria-checked and the roving tab stop \u2014 and the value reaches the form through its group's ElementInternals, because a shadow-root input is never associated with the ancestor form.`,validationBehavior:'None of its own; a group-level invalid flag applies. Requiredness belongs to the group \u2014 "pick one of these" is a statement about the set, not a single card \u2014 and the paint comes via a descendant rule: an invalid group\'s aria-invalid/data-invalid reaches its cards through `.skdx-radio-group[data-invalid] .skdx-radio-card`.',accessibility:{role:"radio \u2014 from the native input, not an ARIA role",keyboard:[{key:"Arrow keys",behavior:'Move between cards and select as they go, handled by RadioGroup. Mirrored under dir="rtl".'},{key:"Tab",behavior:"A single stop for the whole group: only the checked card \u2014 or the first enabled one when nothing is checked \u2014 is tabbable, so a ten-option group costs one tab press rather than ten."},{key:"Space",behavior:"Selects the focused card."}],ariaAttributes:["aria-labelledby on the control pointing at the Label, and only when one is mounted","aria-describedby on the control pointing at the Description, on the same condition","The association must hold in server-rendered markup as well as after hydration, which the registration alone does not give: effects never run on the server, so a control shipped naming nothing has its accessible name silently widened to the card's whole text content \u2014 and for an un-hydrated island that markup is the whole product. It is resolved from what the children declare or from real DOM children one render earlier, with the registration staying the authority once it has run; where no render-time signal exists, explicit hasLabel/hasDescription props (default false) seed the server-rendered guess.","aria-hidden on the Indicator, which draws a state the control already reports",'aria-disabled="true" on the control while disabled, in Web Components only. There the control is an ARIA role="radio" widget rather than a native <input type="radio">, so there is no native disabled attribute to carry the semantics; React, Vue, Angular and Svelte set the native attribute and must not add aria-disabled on top of it.',"data-value on the root, so the selected card is addressable from CSS without reading the input"],focusManagement:"Focus lives on the hidden input, never on the card. Roving tabindex is owned by the group: exactly one card is a tab stop at a time. data-focus-visible is set only for keyboard focus, so a ring appears when it should and not on every click.",namingRequirement:"A Label part, or an explicit aria-label on the control when the card has no visible title. The Description is deliberately excluded from the name, so the announcement stays the title rather than the whole card."},styling:{cssParts:["radio-card","control","indicator","label","description"],dataAttributes:[...e,"data-state","data-disabled","data-focus-visible","data-value","data-size",{name:"data-skdx-radio-card-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[]};export{a as radioCardContract};
1
+ import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"RadioCard",purpose:'A radio with card-sized hit target for RadioGroup, built as a <label> wrapping a hidden <input type="radio">, with Indicator, Label, and Description parts.',props:[{name:"value",type:"string",required:!0,description:"Unique identifier compared against the RadioGroup's selected value."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables this card only; a group-level disabled takes precedence and cannot be re-enabled per card."},{name:"hasLabel",type:"boolean",defaultValue:"false",required:!1,description:"Svelte-only: declares that a Label is present, so server-rendered markup carries aria-labelledby before hydration. Other frameworks detect it during render.",frameworks:["svelte"],exception:"snippet-opaque",frameworksReason:"React and Vue scan children for the part during render; Angular detects it in its first change-detection pass; Web Components queries real DOM children. Svelte receives children as an opaque Snippet, so it takes an explicit prop instead. The real registration on mount is authoritative."},{name:"hasDescription",type:"boolean",defaultValue:"false",required:!1,description:"Svelte-only: declares a Description is present for aria-describedby.",frameworks:["svelte"],exception:"snippet-opaque",frameworksReason:"Same reason as hasLabel: Svelte cannot inspect a Snippet, so it needs an explicit prop."},{name:"label",type:"string",required:!1,description:"Title text, rendered into the Label that `aria-labelledby` points at. When supplied, the card derives Control, Indicator, and Label automatically; omit it and an `aria-label` on the control is required. This all-or-nothing behaviour preserves the anatomy as the card itself, not optional additions."},{name:"description",type:"string",required:!1,description:"Supporting copy under the title, which derives a Description part and `aria-describedby`. The only optional part of the derived anatomy; a card without supporting copy is still a card."},{name:"checked",type:"boolean",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Only the custom element publishes it. The other four hand this derived state down to their parts through context, so a consumer never reads it off a part; here every part is its own element with no such channel, and reading it back is the only way to reflect the state in surrounding markup.",description:"Whether this card currently holds the group's selected value. Read-only: pick it through the group's value. It has no defaultChecked twin, because the seed for an uncontrolled group is RadioGroup's own defaultValue and a second seed on the card would let the two disagree."},{name:"effectiveDisabled",type:"boolean",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Only the custom element publishes it. The other four hand this derived state down to their parts through context, so a consumer never reads it off a part; here every part is its own element with no such channel, and reading it back is the only way to reflect the state in surrounding markup.",description:"Whether this card is disabled, by its own disabled or by the group's. Read-only: it is the OR of the two."},{name:"size",type:"'sm' | 'md' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[],parts:[{name:"Control",description:"The hidden radio input, still focusable so the browser owns activation and focus. Points aria-labelledby at the Label and aria-describedby at the Description when mounted."},{name:"Indicator",description:"The drawn dot or ring, aria-hidden because the control already reports its own checked state."},{name:"Label",description:"The card's title, and the accessible name."},{name:"Description",description:"Supporting copy announced after the name, referenced by aria-describedby so it is pronounced rather than folded into it."}],slots:[{name:"default",description:"The card's body: Control, Indicator, Label, and usually Description."}],appearance:[{attribute:"data-size",values:["sm","md","lg"],stylesheet:"radio-card.css"}],states:["checked","unchecked","focus-visible","disabled"],controlledUncontrolled:"Neither \u2014 RadioCard holds no state of its own. Selection belongs to the enclosing RadioGroup.",formBehavior:`A real <input type="radio"> carrying the group's name in React, Vue, Svelte and Angular, so exclusivity and form submission are native rather than reimplemented. The Web Components Control is an ARIA radio instead \u2014 a visually-hidden element with role="radio", aria-checked and the roving tab stop \u2014 and the value reaches the form through its group's ElementInternals, because a shadow-root input is never associated with the ancestor form.`,validationBehavior:'None of its own; a group-level invalid flag applies. Requiredness belongs to the group \u2014 "pick one of these" is a statement about the set, not a single card \u2014 and the paint comes via a descendant rule: an invalid group\'s aria-invalid/data-invalid reaches its cards through `.skdx-radio-group[data-invalid] .skdx-radio-card`.',accessibility:{role:"radio \u2014 from the native input, not an ARIA role",keyboard:[{key:"Arrow keys",behavior:'Move between cards and select as they go, handled by RadioGroup. Mirrored under dir="rtl".'},{key:"Tab",behavior:"A single stop for the whole group: only the checked card \u2014 or the first enabled one when nothing is checked \u2014 is tabbable, so a ten-option group costs one tab press rather than ten."},{key:"Space",behavior:"Selects the focused card."}],ariaAttributes:["aria-labelledby on the control pointing at the Label, and only when one is mounted","aria-describedby on the control pointing at the Description, on the same condition","The association must hold in server-rendered markup as well as after hydration, which the registration alone does not give: effects never run on the server, so a control shipped naming nothing has its accessible name silently widened to the card's whole text content \u2014 and for an un-hydrated island that markup is the whole product. It is resolved from what the children declare or from real DOM children one render earlier, with the registration staying the authority once it has run; where no render-time signal exists, explicit hasLabel/hasDescription props (default false) seed the server-rendered guess.","aria-hidden on the Indicator, which draws a state the control already reports",'aria-disabled="true" on the control while disabled, in Web Components only. There the control is an ARIA role="radio" widget rather than a native <input type="radio">, so there is no native disabled attribute to carry the semantics; React, Vue, Angular and Svelte set the native attribute and must not add aria-disabled on top of it.',"data-value on the root, so the selected card is addressable from CSS without reading the input"],focusManagement:"Focus lives on the hidden input, never on the card. Roving tabindex is owned by the group: exactly one card is a tab stop at a time. data-focus-visible is set only for keyboard focus, so a ring appears when it should and not on every click.",namingRequirement:"A Label part, or an explicit aria-label on the control when the card has no visible title. The Description is deliberately excluded from the name, so the announcement stays the title rather than the whole card."},styling:{cssParts:["radio-card","control","indicator","label","description"],dataAttributes:[...e,"data-state","data-disabled","data-focus-visible","data-value","data-size",{name:"data-skdx-radio-card-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[],frameworkExtensions:[{framework:"webComponents",kind:"method",name:"select",type:"(reason: RadioGroupChangeReason) => void",description:"Selects this card through the group. A no-op while disabled or already selected, so re-picking emits nothing. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract."},{framework:"webComponents",kind:"method",name:"select",type:"(reason: RadioGroupChangeReason) => void",description:"Selects this card through the group. A no-op while disabled or already selected, so re-picking emits nothing. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract."}]};export{a as radioCardContract};
@@ -1 +1 @@
1
- import{changeDescription as a,changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as t,setCustomValidityMethod as r}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as i}from"./styling-hooks.js";var d={name:"RadioGroup",purpose:"A single-selection group of radio buttons. Manages roving tabindex and arrow-key/Home/End navigation between items \u2014 the checked item (or the first enabled one, when none is checked) is the sole tab stop.",props:[{name:"value",type:"string | null",required:!1,description:'Selected value for controlled usage. null means controlled with nothing chosen; undefined is reserved for "uncontrolled".'},{name:"defaultValue",type:"string | null",required:!1,description:"Initial value for uncontrolled usage; accepts null or undefined."},{name:"name",type:"string",required:!1,description:"Name for form submission via hidden input on each item."},{name:"form",type:"string",required:!1,description:"Form id when group is rendered outside the form."},{name:"required",type:"boolean",defaultValue:"false",required:!1,description:"Require a selection for form validation."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disable all items."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Freeze selection without freezing group. The roving tab stop is unchanged, arrow keys work, nothing gains aria-disabled, and items keep submitting."},{name:"invalid",type:"boolean",defaultValue:"false",required:!1,description:"Mark group invalid; exposed as aria-invalid/data-invalid."},{name:"orientation",type:"'horizontal' | 'vertical' | 'both'",defaultValue:"'both'",required:!1,description:'Arrow-key navigation axis; "both" responds to all four arrows.'},{name:"loop",type:"boolean",defaultValue:"true",required:!1,description:"Arrow-key navigation wraps from last item to first; defaults to true per APG. A segmented ToggleGroup turns looping off instead, because pressing follows focus there and a wrap would silently re-press the item at the other end."},{name:"options",type:"ReadonlyArray<{ value: string; label: string; disabled?: boolean }>",required:!1,description:"Radios the group derives \u2014 one Item per entry with its label. Flat only; no option-group shape."}],events:[{name:"value-change",description:a("the selected item"),detail:e("string")},{name:"value-change-detail",frameworks:["angular"],exception:"event-shape",frameworksReason:"Angular's `valueChange` emits the scalar so `[(value)]` round-trips the value itself; this second output carries the detail object the other four hand to `value-change`.",description:"Fires with valueChange, carrying the previous value and the reason for the change.",detail:e("string")}],parts:[{name:"Item",description:'One radio with role="radio", aria-checked, aria-disabled, data-state, data-disabled, and data-value. Plus hidden input when group has a name.',props:[{name:"checked",type:"boolean",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Only the custom element publishes it. The other four hand this derived state down to their parts through context, so a consumer never reads it off a part; here every part is its own element with no such channel, and reading it back is the only way to reflect the state in surrounding markup.",description:"Whether this item currently holds one of the group's selected values. Read-only: pick or clear it through the root's value."},{name:"value",type:"string",required:!0,description:"Unique identifier compared to group's value; required."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disable this item; skipped by arrow keys and does not claim tab stop."}]},{name:"Indicator",description:"Selection mark inside the checked item; shown only when checked.",props:[{name:"forceMount",type:"boolean",defaultValue:"false",required:!1,description:"Keep mark present when unselected; lets animation libraries animate exits."}]}],slots:[{name:"default",description:"RadioGroupItem children."}],states:["default","readonly"],controlledUncontrolled:'Controlled via value + a change event, or uncontrolled via defaultValue. The controlled/uncontrolled decision is "is value undefined", so null \u2014 not undefined \u2014 is the value that means "controlled, nothing selected". In Angular the pair is split: `valueChange` emits the scalar for `[(value)]`, `valueChangeDetail` carries the detail.',formBehavior:'Each item renders a hidden native <input type="radio"> when the group has a name, in React, Vue, Svelte and Angular. Web Components publishes the selected value through ElementInternals instead, because an input inside the shadow root is never associated with the ancestor form. readOnly keeps items submitting either way.',validationBehavior:"required marks inputs required. checkValidity()/reportValidity() delegate to one hidden input, which answers for the whole group because they all share the group's name. setCustomValidity() writes to the same input. Without a name, both readable methods report valid and the write is dropped. invalid exposes aria-invalid/data-invalid on the root.",accessibility:{role:"radiogroup",keyboard:[{key:"ArrowRight/ArrowDown",behavior:'Move focus to (and select) next item. Mirrored under dir="rtl": ArrowLeft is what moves to the *next* item there.'},{key:"ArrowLeft/ArrowUp",behavior:"Move focus to (and select) previous item."},{key:"Home",behavior:"Move focus to (and select) first item."},{key:"End",behavior:"Move focus to (and select) last item."}],ariaAttributes:["aria-required reflects required","aria-invalid reflects invalid","aria-readonly on root (not aria-disabled)","each item exposes aria-checked, and aria-disabled while it is disabled"],focusManagement:"Roving tabindex \u2014 checked item (or first enabled) is sole tab stop. Arrow keys select; external focus does not. First-enabled fallback is resolved during render.",namingRequirement:'Root must be named via Fieldset legend or aria-label/aria-labelledby. role="radiogroup" does not inherit item labels.'},styling:{cssParts:["radio-group","item","indicator"],dataAttributes:[...i,"data-orientation","data-disabled","data-readonly","data-invalid","data-state","data-value",{name:"data-skdx-radio-group-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[...t,r,{name:"setValue",signature:"(value: string, reason?: RadioGroupChangeReason) => void",frameworks:["webComponents"],exception:"imperative",frameworksReason:"Only the custom element publishes it. The other four re-render with a new value prop and never expose a setter; a Web Components consumer holds the element itself, so the same change is made by calling this method on it.",description:"Selects the item named by value and emits value-change carrying reason."}],frameworkExtensions:[{framework:"webComponents",name:"requiredMessage",type:"string",description:"Custom message shown when the required constraint fails; Web Components only, set from markup with the required-message attribute."}]};export{d as radioGroupContract};
1
+ import{changeDescription as t,changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as a,setCustomValidityMethod as r}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as n}from"./styling-hooks.js";var l={name:"RadioGroup",purpose:"A single-selection group of radio buttons. Manages roving tabindex and arrow-key/Home/End navigation between items \u2014 the checked item (or the first enabled one, when none is checked) is the sole tab stop.",props:[{name:"value",type:"string | null",required:!1,description:'Selected value for controlled usage. null means controlled with nothing chosen; undefined is reserved for "uncontrolled".'},{name:"defaultValue",type:"string | null",required:!1,description:"Initial value for uncontrolled usage; accepts null or undefined."},{name:"name",type:"string",required:!1,description:"Name for form submission via hidden input on each item."},{name:"form",type:"string",required:!1,description:"Form id when group is rendered outside the form."},{name:"required",type:"boolean",defaultValue:"false",required:!1,description:"Require a selection for form validation."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disable all items."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Freeze selection without freezing group. The roving tab stop is unchanged, arrow keys work, nothing gains aria-disabled, and items keep submitting."},{name:"invalid",type:"boolean",defaultValue:"false",required:!1,description:"Mark group invalid; exposed as aria-invalid/data-invalid."},{name:"orientation",type:"'horizontal' | 'vertical' | 'both'",defaultValue:"'both'",required:!1,description:'Arrow-key navigation axis; "both" responds to all four arrows.'},{name:"loop",type:"boolean",defaultValue:"true",required:!1,description:"Arrow-key navigation wraps from last item to first; defaults to true per APG. A segmented ToggleGroup turns looping off instead, because pressing follows focus there and a wrap would silently re-press the item at the other end."},{name:"options",type:"ReadonlyArray<{ value: string; label: string; disabled?: boolean }>",required:!1,description:"Radios the group derives \u2014 one Item per entry with its label. Flat only; no option-group shape."}],events:[{name:"value-change",description:t("the selected item"),detail:e("string")},{name:"value-change-detail",frameworks:["angular"],exception:"event-shape",frameworksReason:"Angular's `valueChange` emits the scalar so `[(value)]` round-trips the value itself; this second output carries the detail object the other four hand to `value-change`.",description:"Fires with valueChange, carrying the previous value and the reason for the change.",detail:e("string")}],parts:[{name:"Item",description:'One radio with role="radio", aria-checked, aria-disabled, data-state, data-disabled, and data-value. Plus hidden input when group has a name.',props:[{name:"checked",type:"boolean",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Only the custom element publishes it. The other four hand this derived state down to their parts through context, so a consumer never reads it off a part; here every part is its own element with no such channel, and reading it back is the only way to reflect the state in surrounding markup.",description:"Whether this item currently holds one of the group's selected values. Read-only: pick or clear it through the root's value."},{name:"value",type:"string",required:!0,description:"Unique identifier compared to group's value; required."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disable this item; skipped by arrow keys and does not claim tab stop."}]},{name:"Indicator",description:"Selection mark inside the checked item; shown only when checked.",props:[{name:"forceMount",type:"boolean",defaultValue:"false",required:!1,description:"Keep mark present when unselected; lets animation libraries animate exits."}]}],slots:[{name:"default",description:"RadioGroupItem children."}],states:["default","readonly"],controlledUncontrolled:'Controlled via value + a change event, or uncontrolled via defaultValue. The controlled/uncontrolled decision is "is value undefined", so null \u2014 not undefined \u2014 is the value that means "controlled, nothing selected". In Angular the pair is split: `valueChange` emits the scalar for `[(value)]`, `valueChangeDetail` carries the detail.',formBehavior:'Each item renders a hidden native <input type="radio"> when the group has a name, in React, Vue, Svelte and Angular. Web Components publishes the selected value through ElementInternals instead, because an input inside the shadow root is never associated with the ancestor form. readOnly keeps items submitting either way.',validationBehavior:"required marks inputs required. checkValidity()/reportValidity() delegate to one hidden input, which answers for the whole group because they all share the group's name. setCustomValidity() writes to the same input. Without a name, both readable methods report valid and the write is dropped. invalid exposes aria-invalid/data-invalid on the root.",accessibility:{role:"radiogroup",keyboard:[{key:"ArrowRight/ArrowDown",behavior:'Move focus to (and select) next item. Mirrored under dir="rtl": ArrowLeft is what moves to the *next* item there.'},{key:"ArrowLeft/ArrowUp",behavior:"Move focus to (and select) previous item."},{key:"Home",behavior:"Move focus to (and select) first item."},{key:"End",behavior:"Move focus to (and select) last item."}],ariaAttributes:["aria-required reflects required","aria-invalid reflects invalid","aria-readonly on root (not aria-disabled)","each item exposes aria-checked, and aria-disabled while it is disabled"],focusManagement:"Roving tabindex \u2014 checked item (or first enabled) is sole tab stop. Arrow keys select; external focus does not. First-enabled fallback is resolved during render.",namingRequirement:'Root must be named via Fieldset legend or aria-label/aria-labelledby. role="radiogroup" does not inherit item labels.'},styling:{cssParts:["radio-group","item","indicator"],dataAttributes:[...n,"data-orientation","data-disabled","data-readonly","data-invalid","data-state","data-value",{name:"data-skdx-radio-group-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[...a,r,{name:"setValue",signature:"(value: string, reason?: RadioGroupChangeReason) => void",frameworks:["webComponents"],exception:"imperative",frameworksReason:"Only the custom element publishes it. The other four re-render with a new value prop and never expose a setter; a Web Components consumer holds the element itself, so the same change is made by calling this method on it.",description:"Selects the item named by value and emits value-change carrying reason."}],frameworkExtensions:[{framework:"webComponents",name:"requiredMessage",type:"string",description:"Custom message shown when the required constraint fails; Web Components only, set from markup with the required-message attribute."},{framework:"webComponents",kind:"method",name:"getSelectedValue",type:"() => string | null | undefined",description:'The currently selected value. `undefined` means uncontrolled; a `null` here is a real "nothing selected" state. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract.'},{framework:"webComponents",kind:"method",name:"getSelectedValue",type:"() => string | null | undefined",description:'The currently selected value. `undefined` means uncontrolled; a `null` here is a real "nothing selected" state. Web Components exposes this documented state/action on its element for native JavaScript and composed elements. Other wrappers coordinate their parts through context, injected state or callback props; this instance helper is separate from the common interaction contract.'}]};export{l as radioGroupContract};
@@ -1 +1 @@
1
- import{changeDescription as t,changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as a,setCustomValidityMethod as i}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as r}from"./styling-hooks.js";var l={name:"Rating",purpose:"Single-selection rating widget (e.g. stars). Highlights hovered/selected item and all before it. Roving-tabindex navigation (horizontal, no wrap); arrows clamp at both ends. The selected item (or 1 when nothing is selected) is the sole tab stop.",props:[{name:"value",type:"number",required:!1,description:"Selected value, for controlled usage."},{name:"defaultValue",type:"number",defaultValue:"0",required:!1,description:"Initial value, for uncontrolled usage.",frameworks:["react","vue","svelte","angular"],exception:"wc-default",frameworksReason:"Web Components has no default-value member and will not grow one: the authored value attribute is the seed, exactly as <input value> seeds a native input and as switch already documents."},{name:"max",type:"number",defaultValue:"5",required:!1,description:"The number of items on the scale and the highest value the rating can hold. In whole-item mode it is a count of radios; with allowHalf it is aria-valuemax on the root slider."},{name:"itemLabel",type:"(value: number, max: number) => string",defaultValue:"(value, max) => `${value} of ${max} stars`",required:!1,description:"Names every item (applied as aria-label). Each item is a role=radio with only an icon, so without this a screen-reader user cannot tell which star they are on. Takes max and value to name the score out of the total. An aria-label on the item itself wins over this."},{name:"allowClear",type:"boolean",defaultValue:"false",required:!1,description:"Lets the committed value clear to 0 by picking it again (clicking the checked item, or pressing Space). Off by default, where re-picking the current value is swallowed and emits nothing. With allowHalf, the leading half clears and the trailing half sets the next value. Clearing fires value-change with 0."},{name:"allowHalf",type:"boolean",defaultValue:"false",required:!1,description:"Allow half-item values (0.5, 1, 1.5, \u2026). Pointer picks from the leading half (trailing under RTL); arrow keys step by 0.5. Changes the ARIA: the root becomes role=slider with aria-valuenow/aria-valuetext, and items become role=presentation."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables every item."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Displays the current value but blocks interaction (still focusable, unlike disabled)."},{name:"name",type:"string",required:!1,description:"Form field name. The selected value is submitted under it; without it the rating submits nothing."},{name:"required",type:"boolean",defaultValue:"false",required:!1,description:"Marks the rating required. valueMissing while the value is 0, as for a radio group."},{name:"form",type:"string",required:!1,description:"Id of the `<form>` that owns this rating, for a rating rendered outside it. Decides which form's reset restores the initial rating."},{name:"icon",type:"unknown",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:"Vue and Web Components take this as a named slot rather than a prop \u2014 Vue because a renderable region is a slot in Vue's own idiom, Web Components because a custom element attribute carries a string and cannot carry a node. The content hole is the same one in all five; only the way a node reaches it differs, and on a custom element markup is the only route in. Both slot spellings are declared under `slots`.",description:"Glyph rendered for each item. Defaults to a built-in star."},{name:"size",type:"'md' | 'sm' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[{name:"value-change",detail:e("number"),description:`${t("the rating")} Re-picking the current value is swallowed unless allowClear is set. An unset rating and a rating of 0 are the same state: 0 is the resting value, what aria-valuenow reports, and what counts as valueMissing when required.`},{name:"value-change-detail",frameworks:["angular"],exception:"event-shape",frameworksReason:"Angular's `valueChange` emits the scalar so `[(value)]` round-trips the value itself; this second output carries the detail object the other four hand to `value-change`.",description:"Fires with valueChange, carrying the previous rating and the reason for the change.",detail:e("number")},{name:"hover-value-change",description:"Fires with the value the pointer is previewing, or null when it leaves. Display-only \u2014 the preview never writes the committed value.",detail:"number | null"}],parts:[{name:"Item",description:'One position on the scale (e.g. a star). A role="radio" in whole-item mode, or role="presentation" when allowHalf turns the root into role="slider". Publishes data-state, data-highlighted (on itself and all items before it), and data-half. Clicking, focusing or hovering commits or previews its value; with allowHalf, the leading half commits value - 0.5.',props:[{name:"value",type:"number",required:!0,description:"This item's 1-indexed position on the scale. Determines what the item commits when picked and what itemLabel is called with."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Excludes this item from selection, hover preview and roving focus. OR-ed with the root's disabled, not overriding it."}]}],slots:[{name:"default",description:"RatingItem children, one per position."},{name:"item",description:"A single RatingItem's content, e.g. a star icon."},{name:"icon",description:"The glyph rendered for each item in the rating."}],appearance:[{attribute:"data-size",values:["md","sm","lg"],stylesheet:"rating.css"}],states:["default","checked","unchecked","highlighted","half","disabled"],controlledUncontrolled:"Supports both: value + onValueChange (controlled) or defaultValue (uncontrolled). In Angular the pair is split: `valueChange` emits the scalar for `[(value)]`, `valueChangeDetail` carries the detail.",formBehavior:'Form-associated: with a name, the selected value is submitted. Like a radio group, a rating of 0 carries no value and is valueMissing when required \u2014 Web Components submit no entry at all there, while React/Vue/Svelte/Angular submit one empty entry, because a mirrored input that is not rendered cannot report valueMissing (the same trade-off the Select contract makes for an empty selection). With allowHalf the tab stop is the root div role="slider" rather than the item buttons, so a <fieldset disabled> ancestor stops submission (the mirrored input is disabled) but does not disable the root itself \u2014 pass disabled explicitly there. Web Components has no default-value member and will not grow one: the authored value attribute is the seed, exactly as <input value> seeds a native input and as switch already documents. A form reset restores the initial rating \u2014 silently in Web Components, and by firing value-change in React/Vue/Svelte/Angular, where a controlled value belongs to the parent and would otherwise be restored straight back.',validationBehavior:"required participates in native constraint validation, reported through checkValidity()/reportValidity(). setCustomValidity() writes the third half: an error the browser cannot derive \u2014 a server response, a cross-field rule \u2014 lands on the same control that carries the constraint, so checkValidity() sees it and reportValidity() shows it, and '' clears it. Dropped without a name, matching the reads, since there is then no control and nothing is submitted. One Web-Components-only attribute sits alongside this and is deliberately NOT contracted: required-message, which replaces the English wording the browser shows for a failed required constraint. It is not a Rating capability \u2014 it comes from the FormAssociated mixin and every form-associated skdx element carries it \u2014 and it exists there because that string is sealed inside a shadow root, a problem the other four packages do not have since their control is a native input in the consumer's own tree. See the ruling in form-validity.ts, decided once for Rating, Switch, ToggleGroup and Checkbox.",accessibility:{role:'radiogroup (root), radio (each item) \u2014 or slider (root), role="presentation" (each item) when allowHalf is set',keyboard:[{key:"ArrowRight/ArrowDown",behavior:"Moves focus (and selection) to the next item; with allowHalf, increases the value by 0.5."},{key:"ArrowLeft/ArrowUp",behavior:"Moves focus (and selection) to the previous item; with allowHalf, decreases the value by 0.5."},{key:"Home",behavior:"Sets the value to 0, in every mode, as the slider pattern requires."},{key:"End",behavior:"Sets the value to max, in every mode, as the slider pattern requires."},{key:"Space",behavior:"Whole-item mode: commits the focused item's value, or clears to 0 if already committed and allowClear is set. In allowHalf mode, items are not focusable and Space does nothing."},{key:"Enter",behavior:"Suppressed. Enter on a focused item would activate the native button and submit the form; Space is the activation key here."}],ariaAttributes:["aria-checked on each item (whole-item mode)","aria-label on each item (whole-item mode), from itemLabel(value, max) \u2014 unless the item carries its own aria-label","aria-valuemin/aria-valuemax/aria-valuenow/aria-valuetext/aria-orientation on the root (allowHalf)",'role="presentation" on each item (allowHalf) \u2014 non-focusable span carrying pointer handlers',"aria-disabled/aria-readonly on the root"],focusManagement:"Whole-item mode: roving tabindex on the checked item (or 1 when none is checked). With allowHalf: the root is the sole tab stop and items leave the tab order.",namingRequirement:"The root needs an accessible name in both modes (aria-label or aria-labelledby) \u2014 neither radiogroup nor slider takes its name from content."},styling:{cssParts:["rating","item"],dataAttributes:[...r,"data-disabled","data-readonly","data-state","data-highlighted","data-half","data-size","data-skdx-rating-item",{name:"data-skdx-rating-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[...a,i],frameworkExtensions:[{framework:"webComponents",name:"requiredMessage",type:"string",description:"Replaces the browser's message when a required rating is submitted empty. Web-Components-only, from the FormAssociated mixin, and set from markup with the required-message attribute. The other four expose `setCustomValidity()` on the hidden input in the consumer's tree."}]};export{l as ratingContract};
1
+ import{changeDescription as t,changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as a,setCustomValidityMethod as i}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as r}from"./styling-hooks.js";var l={name:"Rating",purpose:"Single-selection rating widget (e.g. stars). Highlights hovered/selected item and all before it. Roving-tabindex navigation (horizontal, no wrap); arrows clamp at both ends. The selected item (or 1 when nothing is selected) is the sole tab stop.",props:[{name:"value",type:"number",required:!1,description:"Selected value, for controlled usage."},{name:"defaultValue",type:"number",defaultValue:"0",required:!1,description:"Initial value, for uncontrolled usage.",frameworks:["react","vue","svelte","angular"],exception:"wc-default",frameworksReason:"Web Components has no default-value member and will not grow one: the authored value attribute is the seed, exactly as <input value> seeds a native input and as switch already documents."},{name:"max",type:"number",defaultValue:"5",required:!1,description:"The number of items on the scale and the highest value the rating can hold. In whole-item mode it is a count of radios; with allowHalf it is aria-valuemax on the root slider."},{name:"itemLabel",type:"(value: number, max: number) => string",defaultValue:"(value, max) => `${value} of ${max} stars`",required:!1,description:"Names every item (applied as aria-label). Each item is a role=radio with only an icon, so without this a screen-reader user cannot tell which star they are on. Takes max and value to name the score out of the total. An aria-label on the item itself wins over this."},{name:"allowClear",type:"boolean",defaultValue:"false",required:!1,description:"Lets the committed value clear to 0 by picking it again (clicking the checked item, or pressing Space). Off by default, where re-picking the current value is swallowed and emits nothing. With allowHalf, the leading half clears and the trailing half sets the next value. Clearing fires value-change with 0."},{name:"allowHalf",type:"boolean",defaultValue:"false",required:!1,description:"Allow half-item values (0.5, 1, 1.5, \u2026). Pointer picks from the leading half (trailing under RTL); arrow keys step by 0.5. Changes the ARIA: the root becomes role=slider with aria-valuenow/aria-valuetext, and items become role=presentation."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Disables every item."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Displays the current value but blocks interaction (still focusable, unlike disabled)."},{name:"name",type:"string",required:!1,description:"Form field name. The selected value is submitted under it; without it the rating submits nothing."},{name:"required",type:"boolean",defaultValue:"false",required:!1,description:"Marks the rating required. valueMissing while the value is 0, as for a radio group."},{name:"form",type:"string",required:!1,description:"Id of the `<form>` that owns this rating, for a rating rendered outside it. Decides which form's reset restores the initial rating."},{name:"icon",type:"unknown",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:"Vue and Web Components take this as a named slot rather than a prop \u2014 Vue because a renderable region is a slot in Vue's own idiom, Web Components because a custom element attribute carries a string and cannot carry a node. The content hole is the same one in all five; only the way a node reaches it differs, and on a custom element markup is the only route in. Both slot spellings are declared under `slots`.",description:"Glyph rendered for each item. Defaults to a built-in star."},{name:"size",type:"'md' | 'sm' | 'lg' | (string & {})",required:!1,description:"Visual size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[{name:"value-change",detail:e("number"),description:`${t("the rating")} Re-picking the current value is swallowed unless allowClear is set. An unset rating and a rating of 0 are the same state: 0 is the resting value, what aria-valuenow reports, and what counts as valueMissing when required.`},{name:"value-change-detail",frameworks:["angular"],exception:"event-shape",frameworksReason:"Angular's `valueChange` emits the scalar so `[(value)]` round-trips the value itself; this second output carries the detail object the other four hand to `value-change`.",description:"Fires with valueChange, carrying the previous rating and the reason for the change.",detail:e("number")},{name:"hover-value-change",description:"Fires with the value the pointer is previewing, or null when it leaves. Display-only \u2014 the preview never writes the committed value.",detail:"number | null"}],parts:[{name:"Item",description:'One position on the scale (e.g. a star). A role="radio" in whole-item mode, or role="presentation" when allowHalf turns the root into role="slider". Publishes data-state, data-highlighted (on itself and all items before it), and data-half. Clicking, focusing or hovering commits or previews its value; with allowHalf, the leading half commits value - 0.5.',props:[{name:"value",type:"number",required:!0,description:"This item's 1-indexed position on the scale. Determines what the item commits when picked and what itemLabel is called with."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Excludes this item from selection, hover preview and roving focus. OR-ed with the root's disabled, not overriding it."}],slots:[{name:"default",description:"A single RatingItem's content, e.g. a star icon."}]}],slots:[{name:"default",description:"RatingItem children, one per position."},{name:"icon",description:"The glyph rendered for each item in the rating."}],appearance:[{attribute:"data-size",values:["md","sm","lg"],stylesheet:"rating.css"}],states:["default","checked","unchecked","highlighted","half","disabled"],controlledUncontrolled:"Supports both: value + onValueChange (controlled) or defaultValue (uncontrolled). In Angular the pair is split: `valueChange` emits the scalar for `[(value)]`, `valueChangeDetail` carries the detail.",formBehavior:'Form-associated: with a name, the selected value is submitted. Like a radio group, a rating of 0 carries no value and is valueMissing when required \u2014 Web Components submit no entry at all there, while React/Vue/Svelte/Angular submit one empty entry, because a mirrored input that is not rendered cannot report valueMissing (the same trade-off the Select contract makes for an empty selection). With allowHalf the tab stop is the root div role="slider" rather than the item buttons, so a <fieldset disabled> ancestor stops submission (the mirrored input is disabled) but does not disable the root itself \u2014 pass disabled explicitly there. Web Components has no default-value member and will not grow one: the authored value attribute is the seed, exactly as <input value> seeds a native input and as switch already documents. A form reset restores the initial rating \u2014 silently in Web Components, and by firing value-change in React/Vue/Svelte/Angular, where a controlled value belongs to the parent and would otherwise be restored straight back.',validationBehavior:"required participates in native constraint validation, reported through checkValidity()/reportValidity(). setCustomValidity() writes the third half: an error the browser cannot derive \u2014 a server response, a cross-field rule \u2014 lands on the same control that carries the constraint, so checkValidity() sees it and reportValidity() shows it, and '' clears it. Dropped without a name, matching the reads, since there is then no control and nothing is submitted. One Web-Components-only attribute sits alongside this and is deliberately NOT contracted: required-message, which replaces the English wording the browser shows for a failed required constraint. It is not a Rating capability \u2014 it comes from the FormAssociated mixin and every form-associated skdx element carries it \u2014 and it exists there because that string is sealed inside a shadow root, a problem the other four packages do not have since their control is a native input in the consumer's own tree. See the ruling in form-validity.ts, decided once for Rating, Switch, ToggleGroup and Checkbox.",accessibility:{role:'radiogroup (root), radio (each item) \u2014 or slider (root), role="presentation" (each item) when allowHalf is set',keyboard:[{key:"ArrowRight/ArrowDown",behavior:"Moves focus (and selection) to the next item; with allowHalf, increases the value by 0.5."},{key:"ArrowLeft/ArrowUp",behavior:"Moves focus (and selection) to the previous item; with allowHalf, decreases the value by 0.5."},{key:"Home",behavior:"Sets the value to 0, in every mode, as the slider pattern requires."},{key:"End",behavior:"Sets the value to max, in every mode, as the slider pattern requires."},{key:"Space",behavior:"Whole-item mode: commits the focused item's value, or clears to 0 if already committed and allowClear is set. In allowHalf mode, items are not focusable and Space does nothing."},{key:"Enter",behavior:"Suppressed. Enter on a focused item would activate the native button and submit the form; Space is the activation key here."}],ariaAttributes:["aria-checked on each item (whole-item mode)","aria-label on each item (whole-item mode), from itemLabel(value, max) \u2014 unless the item carries its own aria-label","aria-valuemin/aria-valuemax/aria-valuenow/aria-valuetext/aria-orientation on the root (allowHalf)",'role="presentation" on each item (allowHalf) \u2014 non-focusable span carrying pointer handlers',"aria-disabled/aria-readonly on the root"],focusManagement:"Whole-item mode: roving tabindex on the checked item (or 1 when none is checked). With allowHalf: the root is the sole tab stop and items leave the tab order.",namingRequirement:"The root needs an accessible name in both modes (aria-label or aria-labelledby) \u2014 neither radiogroup nor slider takes its name from content."},styling:{cssParts:["rating","item"],dataAttributes:[...r,"data-disabled","data-readonly","data-state","data-highlighted","data-half","data-size","data-skdx-rating-item",{name:"data-skdx-rating-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:[]},publicMethods:[...a,i],frameworkExtensions:[{framework:"webComponents",name:"requiredMessage",type:"string",description:"Replaces the browser's message when a required rating is submitted empty. Web-Components-only, from the FormAssociated mixin, and set from markup with the required-message attribute. The other four expose `setCustomValidity()` on the hidden input in the consumer's tree."}]};export{l as ratingContract};
@@ -1 +1 @@
1
- import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var t={name:"Resizable",purpose:"A split-panel layout with draggable, keyboard-operable handles. Panels and handles form an alternating set that may change after mount: every Handle resolves its own index from its position in the DOM at the moment of interaction. A drag redistributes size ONLY between the two panels immediately adjacent to the handle \u2014 the predictable default. Composed of root, Panel, and Handle parts.",props:[{name:"step",type:"number",defaultValue:"2",required:!1,description:"Percentage points per Arrow key press. Group-level so a multi-panel group does not repeat the value silently. Overridable per Handle. The unit is percentage points, not pixels."},{name:"orientation",type:"'horizontal' | 'vertical'",defaultValue:"'horizontal'",required:!1,description:"Layout axis: flex direction, arrow-key pair, cursor, aria-orientation. Exposed as data-orientation. Optional."},{name:"sizes",type:"number[]",required:!1,description:"Controlled panel sizes (percentages, Panel order). Supplied: group renders and reports through `layout`/`layout-commit`. Omitted: uses each Panel's defaultSize."},{name:"panels",type:"ReadonlyArray<{ defaultSize?: number; minSize?: number; maxSize?: number; collapsible?: boolean; collapsedSize?: number; id?: string; content?: unknown }>",required:!1,description:"The panes the group derives \u2014 one Panel per entry. Composition is optional."},{name:"panelTemplate",frameworks:["angular"],exception:"render-prop",frameworksReason:"Angular's `panels[].content` can only be a string, because a component input cannot carry template markup. The other four put markup straight into that entry \u2014 a ReactNode, a VNode, a snippet, or a Node \u2014 so they need no second input; Angular reaches the same capability with an *ngTemplateOutlet fed by this TemplateRef.",type:"TemplateRef<{ $implicit: PanelItem; panel: PanelItem; index: number }>",required:!1,description:"Template rendered as each derived panel's body, replacing that entry's content string."},{name:"size",type:"'sm' | 'lg' | (string & {})",required:!1,description:"Handle target-size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[{name:"layout",description:"Fires with sizes array (percentages) on every pointermove that moves the split.",detail:"number[]"},{name:"layout-commit",description:"Fires with sizes array (percentages) when the user finishes: on pointerup/pointercancel or after keyboard nudge. The event to persist on.",detail:"number[]"},{name:"collapse",description:"Fires when a collapsible Panel reaches collapsedSize, carrying the panel id.",detail:"{ panelId: string }"},{name:"expand",description:"Fires when a collapsed Panel reopens, carrying the panel id. Reachable by pointer and arrow key.",detail:"{ panelId: string }"}],parts:[{name:"Panel",description:"One resizable pane. Registers size constraints on mount and is sized thereafter. No per-Panel `size` prop. Lays out through flexbox before the group computes a size.",props:[{name:"defaultSize",type:"number | undefined",required:!1,description:"Initial size as a percentage of the group's main axis (0-100). Omit to split remaining space evenly. Percentages are the only unit accepted."},{name:"minSize",type:"number",defaultValue:"10",required:!1,description:"Minimum size as a percentage (0-100). Default 10 prevents accidental loss."},{name:"maxSize",type:"number",defaultValue:"90",required:!1,description:"Maximum size as a percentage (0-100). Default 90 pairs with minSize 10."},{name:"collapsible",type:"boolean",defaultValue:"false",required:!1,description:"Lets this panel close: below minSize snaps to collapsedSize. Off by default. Reopens on any movement."},{name:"collapsedSize",type:"number",defaultValue:"0",required:!1,description:"Size (percentage) while closed. Panel carries data-collapsed; group fires collapse/expand."}]},{name:"Handle",description:'The draggable divider (role="separator"), its own tab stop. Carries data-dragging during pointer gestures.',props:[{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:'Refuses every drag and every key. Focusable-and-inert: the handle keeps its tab stop and announces aria-disabled="true".'},{name:"step",type:"number | undefined",defaultValue:"undefined \u2014 inherits the group's step (2)",required:!1,description:"Nudge per Arrow key press, overriding the group's step. Percentage points, not pixels."}]}],slots:[{name:"default",description:"Root content \u2014 alternating Panel, Handle, Panel, ... entries."}],appearance:[{attribute:"data-size",values:["sm","lg"],stylesheet:"resizable.css"}],states:["disabled","dragging","collapsed"],controlledUncontrolled:"Standard. Uncontrolled: sizes seeded from each Panel's defaultSize. Controlled: supply `sizes` for a pure renderer. No `defaultSizes` on root.",formBehavior:"Not applicable \u2014 Resizable does not participate in forms.",validationBehavior:"Not applicable \u2014 Resizable carries no value and participates in no validation.",accessibility:{role:"(none, root); separator (Handle)",keyboard:[{key:"ArrowLeft/ArrowRight (horizontal groups) or ArrowUp/ArrowDown (vertical groups)",behavior:"Nudges the focused Handle by step (default 2 percentage points)."},{key:"Home",behavior:"Shrinks the Handle's left/top panel to its minSize."},{key:"End",behavior:"Grows the Handle's left/top panel to its maxSize."}],ariaAttributes:["Handle's aria-orientation is perpendicular to the group's direction","Handle's aria-valuenow/aria-valuemin/aria-valuemax describe the adjacent panel size; a collapsible panel reports collapsedSize as its aria-valuemin, since that is the smallest size it can legally reach","Handle's aria-valuetext expresses size as a percentage","Handle's aria-controls is the DOM id of the start-side panel","data-dragging on Handle during pointer gesture",'data-disabled on disabled Handle with aria-disabled="true"',"aria-label or aria-labelledby on Handle, forwarded to separator element"],namingRequirement:"Groups with multiple Handles MUST name each distinctly with aria-label/aria-labelledby. Single Handles need no name.",focusManagement:"Each Handle is its own tab stop (tabIndex 0), including while disabled. Disabled Handles are focusable-and-inert."},styling:{cssParts:["panel","handle","root"],dataAttributes:[...e,"data-orientation","data-disabled","data-dragging","data-collapsed","data-size","data-skdx-resizable-handle","data-skdx-resizable-panel",{name:"data-skdx-resizable-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-resizable-handle-size"]},publicMethods:[{name:"getSizes",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React uses `useResizableContext`; see contextApi below.",signature:"() => number[]",description:"Current sizes as percentages, in Panel order."},{name:"setSizes",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React uses `useResizableContext`; see contextApi below.",signature:"(sizes: number[]) => void",description:"Drive layout to sizes (percentages, Panel order). Clamped and renormalized to 100."},{name:"resetSizes",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React uses `useResizableContext`; see contextApi below.",signature:"() => void",description:"Return every panel to its defaultSize or even split: fresh mount layout."}],contextApi:[{name:"getSizes",signature:"() => number[]",description:"Current sizes as percentages, in Panel order.",accessors:{react:"useResizableContext('MyComponent').getSizes",vue:"inject(RESIZABLE_CONTEXT).getSizes",svelte:"requireContext(RESIZABLE_CONTEXT).getSizes",angular:"inject(SkdxResizableState).getSizes",webComponents:"document.querySelector('skdx-resizable-panel-group').getSizes"}},{name:"setSizes",signature:"(sizes: number[]) => void",description:"Drive layout to sizes, clamped and renormalized to 100.",accessors:{react:"useResizableContext('MyComponent').setSizes",vue:"inject(RESIZABLE_CONTEXT).setSizes",svelte:"requireContext(RESIZABLE_CONTEXT).setSizes",angular:"inject(SkdxResizableState).setSizes",webComponents:"document.querySelector('skdx-resizable-panel-group').setSizes"}},{name:"resetSizes",signature:"() => void",description:"Reset every panel to defaultSize or even split.",accessors:{react:"useResizableContext('MyComponent').resetSizes",vue:"inject(RESIZABLE_CONTEXT).resetSizes",svelte:"requireContext(RESIZABLE_CONTEXT).resetSizes",angular:"inject(SkdxResizableState).resetSizes",webComponents:"document.querySelector('skdx-resizable-panel-group').resetSizes"}}]};export{t as resizableContract};
1
+ import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var t={name:"Resizable",purpose:"A split-panel layout with draggable, keyboard-operable handles. Panels and handles form an alternating set that may change after mount: every Handle resolves its own index from its position in the DOM at the moment of interaction. A drag redistributes size ONLY between the two panels immediately adjacent to the handle \u2014 the predictable default. Composed of root, Panel, and Handle parts.",props:[{name:"step",type:"number",defaultValue:"2",required:!1,description:"Percentage points per Arrow key press. Group-level so a multi-panel group does not repeat the value silently. Overridable per Handle. The unit is percentage points, not pixels."},{name:"orientation",type:"'horizontal' | 'vertical'",defaultValue:"'horizontal'",required:!1,description:"Layout axis: flex direction, arrow-key pair, cursor, aria-orientation. Exposed as data-orientation. Optional."},{name:"sizes",type:"number[]",required:!1,description:"Controlled panel sizes (percentages, Panel order). Supplied: group renders and reports through `layout`/`layout-commit`. Omitted: uses each Panel's defaultSize."},{name:"panels",type:"ReadonlyArray<{ defaultSize?: number; minSize?: number; maxSize?: number; collapsible?: boolean; collapsedSize?: number; id?: string; content?: unknown }>",required:!1,description:"The panes the group derives \u2014 one Panel per entry. Composition is optional."},{name:"panelTemplate",frameworks:["angular"],exception:"render-prop",frameworksReason:"Angular's `panels[].content` can only be a string, because a component input cannot carry template markup. The other four put markup straight into that entry \u2014 a ReactNode, a VNode, a snippet, or a Node \u2014 so they need no second input; Angular reaches the same capability with an *ngTemplateOutlet fed by this TemplateRef.",type:"TemplateRef<{ $implicit: PanelItem; panel: PanelItem; index: number }>",required:!1,description:"Template rendered as each derived panel's body, replacing that entry's content string."},{name:"size",type:"'sm' | 'lg' | (string & {})",required:!1,description:"Handle target-size rung, written to data-size on the root; the data-size attribute may be set directly instead."}],events:[{name:"layout",description:"Fires with sizes array (percentages) on every pointermove that moves the split.",detail:"number[]"},{name:"layout-commit",description:"Fires with sizes array (percentages) when the user finishes: on pointerup/pointercancel or after keyboard nudge. The event to persist on.",detail:"number[]"},{name:"collapse",description:"Fires when a collapsible Panel reaches collapsedSize, carrying the panel id.",detail:"{ panelId: string }"},{name:"expand",description:"Fires when a collapsed Panel reopens, carrying the panel id. Reachable by pointer and arrow key.",detail:"{ panelId: string }"}],parts:[{name:"Panel",description:"One resizable pane. Registers size constraints on mount and is sized thereafter. No per-Panel `size` prop. Lays out through flexbox before the group computes a size.",props:[{name:"defaultSize",type:"number | undefined",required:!1,description:"Initial size as a percentage of the group's main axis (0-100). Omit to split remaining space evenly. Percentages are the only unit accepted."},{name:"minSize",type:"number",defaultValue:"10",required:!1,description:"Minimum size as a percentage (0-100). Default 10 prevents accidental loss."},{name:"maxSize",type:"number",defaultValue:"90",required:!1,description:"Maximum size as a percentage (0-100). Default 90 pairs with minSize 10."},{name:"collapsible",type:"boolean",defaultValue:"false",required:!1,description:"Lets this panel close: below minSize snaps to collapsedSize. Off by default. Reopens on any movement."},{name:"collapsedSize",type:"number",defaultValue:"0",required:!1,description:"Size (percentage) while closed. Panel carries data-collapsed; group fires collapse/expand."}]},{name:"Handle",description:'The draggable divider (role="separator"), its own tab stop. Carries data-dragging during pointer gestures.',props:[{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:'Refuses every drag and every key. Focusable-and-inert: the handle keeps its tab stop and announces aria-disabled="true".'},{name:"step",type:"number | undefined",defaultValue:"undefined \u2014 inherits the group's step (2)",required:!1,description:"Nudge per Arrow key press, overriding the group's step. Percentage points, not pixels."}]}],slots:[{name:"default",description:"Root content \u2014 alternating Panel, Handle, Panel, ... entries."}],appearance:[{attribute:"data-size",values:["sm","lg"],stylesheet:"resizable.css"}],states:["disabled","dragging","collapsed"],controlledUncontrolled:"Standard. Uncontrolled: sizes seeded from each Panel's defaultSize. Controlled: supply `sizes` for a pure renderer. No `defaultSizes` on root.",formBehavior:"Not applicable \u2014 Resizable does not participate in forms.",validationBehavior:"Not applicable \u2014 Resizable carries no value and participates in no validation.",accessibility:{role:"(none, root); separator (Handle)",keyboard:[{key:"ArrowLeft/ArrowRight (horizontal groups) or ArrowUp/ArrowDown (vertical groups)",behavior:"Nudges the focused Handle by step (default 2 percentage points)."},{key:"Home",behavior:"Shrinks the Handle's left/top panel to its minSize."},{key:"End",behavior:"Grows the Handle's left/top panel to its maxSize."}],ariaAttributes:["Handle's aria-orientation is perpendicular to the group's direction","Handle's aria-valuenow/aria-valuemin/aria-valuemax describe the adjacent panel size; a collapsible panel reports collapsedSize as its aria-valuemin, since that is the smallest size it can legally reach","Handle's aria-valuetext expresses size as a percentage","Handle's aria-controls is the DOM id of the start-side panel","data-dragging on Handle during pointer gesture",'data-disabled on disabled Handle with aria-disabled="true"',"aria-label or aria-labelledby on Handle, forwarded to separator element"],namingRequirement:"Groups with multiple Handles MUST name each distinctly with aria-label/aria-labelledby. Single Handles need no name.",focusManagement:"Each Handle is its own tab stop (tabIndex 0), including while disabled. Disabled Handles are focusable-and-inert."},styling:{cssParts:["panel","handle","root"],dataAttributes:[...e,"data-orientation","data-disabled","data-dragging","data-collapsed","data-size","data-skdx-resizable-handle","data-skdx-resizable-panel",{name:"data-skdx-resizable-generated",frameworks:["webComponents"],exception:"wc-marker",frameworksReason:'Marks a node the element generated for itself rather than one the consumer authored. React, Vue, Svelte and Angular re-render their derived anatomy declaratively, so "mine or theirs" is answered by the framework and no marker is needed.'}],cssCustomProperties:["--skdx-resizable-handle-size"]},publicMethods:[{name:"getSizes",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React uses `useResizableContext`; see contextApi below.",signature:"() => number[]",description:"Current sizes as percentages, in Panel order."},{name:"setSizes",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React uses `useResizableContext`; see contextApi below.",signature:"(sizes: number[]) => void",description:"Drive layout to sizes (percentages, Panel order). Clamped and renormalized to 100."},{name:"resetSizes",frameworks:["vue","svelte","angular","webComponents"],exception:"imperative",frameworksReason:"React uses `useResizableContext`; see contextApi below.",signature:"() => void",description:"Return every panel to its defaultSize or even split: fresh mount layout."}],contextApi:[{name:"getSizes",signature:"() => number[]",description:"Current sizes as percentages, in Panel order.",accessors:{react:"useResizableContext('MyComponent').getSizes",vue:"inject(RESIZABLE_CONTEXT).getSizes",svelte:"requireContext(RESIZABLE_CONTEXT).getSizes",angular:"inject(SkdxResizableState).getSizes",webComponents:"document.querySelector('skdx-resizable-panel-group').getSizes"}},{name:"setSizes",signature:"(sizes: number[]) => void",description:"Drive layout to sizes, clamped and renormalized to 100.",accessors:{react:"useResizableContext('MyComponent').setSizes",vue:"inject(RESIZABLE_CONTEXT).setSizes",svelte:"requireContext(RESIZABLE_CONTEXT).setSizes",angular:"inject(SkdxResizableState).setSizes",webComponents:"document.querySelector('skdx-resizable-panel-group').setSizes"}},{name:"resetSizes",signature:"() => void",description:"Reset every panel to defaultSize or even split.",accessors:{react:"useResizableContext('MyComponent').resetSizes",vue:"inject(RESIZABLE_CONTEXT).resetSizes",svelte:"requireContext(RESIZABLE_CONTEXT).resetSizes",angular:"inject(SkdxResizableState).resetSizes",webComponents:"document.querySelector('skdx-resizable-panel-group').resetSizes"}}],frameworkExtensions:[{framework:"vue",kind:"slot",name:"panel",type:"Content projection",description:"Panel body for markup `panels` content cannot express. This framework spells the content customization as a native named slot/snippet on this part; consumers of other wrappers compose the corresponding part or use their documented rendering property."},{framework:"vue",kind:"slot",name:"panel",type:"Content projection",description:"Panel body for markup `panels` content cannot express. This framework spells the content customization as a native named slot/snippet on this part; consumers of other wrappers compose the corresponding part or use their documented rendering property."}]};export{t as resizableContract};