@skdx/docs-kit 0.38.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.
- package/dist/data/angular-inputs.json +1 -1
- package/dist/data/api-manifest.json +1 -1
- package/dist/shared/contracts/accordion.js +1 -1
- package/dist/shared/contracts/alert.js +1 -1
- package/dist/shared/contracts/autocomplete.d.ts +1 -1
- package/dist/shared/contracts/autocomplete.js +1 -1
- package/dist/shared/contracts/avatar.js +1 -1
- package/dist/shared/contracts/breadcrumb.js +1 -1
- package/dist/shared/contracts/button.js +1 -1
- package/dist/shared/contracts/calendar.js +1 -1
- package/dist/shared/contracts/card.js +1 -1
- package/dist/shared/contracts/carousel.js +1 -1
- package/dist/shared/contracts/checkbox-card.js +1 -1
- package/dist/shared/contracts/checkbox-group.js +1 -1
- package/dist/shared/contracts/collapsible.js +1 -1
- package/dist/shared/contracts/combobox.js +1 -1
- package/dist/shared/contracts/command.js +1 -1
- package/dist/shared/contracts/dashboard-layout.js +1 -1
- package/dist/shared/contracts/date-picker.js +1 -1
- package/dist/shared/contracts/dialog.d.ts +1 -1
- package/dist/shared/contracts/dialog.js +1 -1
- package/dist/shared/contracts/drawer.d.ts +1 -1
- package/dist/shared/contracts/drawer.js +1 -1
- package/dist/shared/contracts/field.js +1 -1
- package/dist/shared/contracts/file-upload.js +1 -1
- package/dist/shared/contracts/floating-panel.js +1 -1
- package/dist/shared/contracts/form.js +1 -1
- package/dist/shared/contracts/image.js +1 -1
- package/dist/shared/contracts/listbox.js +1 -1
- package/dist/shared/contracts/loading-overlay.js +1 -1
- package/dist/shared/contracts/mentions.js +1 -1
- package/dist/shared/contracts/menu.js +1 -1
- package/dist/shared/contracts/menubar.js +1 -1
- package/dist/shared/contracts/navigation-menu.js +1 -1
- package/dist/shared/contracts/page-container.js +1 -1
- package/dist/shared/contracts/pagination.js +1 -1
- package/dist/shared/contracts/password-input.js +1 -1
- package/dist/shared/contracts/popover.js +1 -1
- package/dist/shared/contracts/progress.js +1 -1
- package/dist/shared/contracts/qr-code.js +1 -1
- package/dist/shared/contracts/radio-card.js +1 -1
- package/dist/shared/contracts/radio-group.js +1 -1
- package/dist/shared/contracts/rating.js +1 -1
- package/dist/shared/contracts/resizable.js +1 -1
- package/dist/shared/contracts/scroll-area.js +1 -1
- package/dist/shared/contracts/select.js +1 -1
- package/dist/shared/contracts/sign-up-page.js +1 -1
- package/dist/shared/contracts/slider.js +1 -1
- package/dist/shared/contracts/speed-dial.js +1 -1
- package/dist/shared/contracts/split-button.js +1 -1
- package/dist/shared/contracts/stepper.js +1 -1
- package/dist/shared/contracts/switch.js +1 -1
- package/dist/shared/contracts/table.js +3 -1
- package/dist/shared/contracts/tabs.js +1 -1
- package/dist/shared/contracts/tag-group.js +1 -1
- package/dist/shared/contracts/tags-input.js +1 -1
- package/dist/shared/contracts/text-field.js +1 -1
- package/dist/shared/contracts/textarea.js +1 -1
- package/dist/shared/contracts/timeline.js +1 -1
- package/dist/shared/contracts/toast.js +9 -1
- package/dist/shared/contracts/toolbar.js +1 -1
- package/dist/shared/contracts/tour.js +1 -1
- package/dist/shared/contracts/transfer.js +1 -1
- package/dist/shared/contracts/tree-select.js +1 -1
- package/dist/shared/contracts/tree.js +1 -1
- package/dist/shared/contracts/types.d.ts +2 -0
- package/dist/shared/contracts/verification-page.js +1 -1
- package/dist/shared/contracts/workspace-switcher.js +1 -1
- package/dist/shared/core/field-core.d.ts +2 -0
- package/dist/shared/core/field-core.js +1 -1
- package/dist/shared/core/focus-targets.js +1 -1
- package/dist/shared/core/label-activation.js +1 -1
- package/dist/skins-src/_foundation-fallback.css +2 -1
- package/dist/skins-src/components/_actuator.css +1 -1
- package/dist/skins-src/components/_card-control.css +1 -1
- package/dist/skins-src/components/_collection.css +2 -2
- package/dist/skins-src/components/_overlay.css +1 -1
- package/dist/skins-src/components/_trigger.css +1 -1
- package/dist/skins-src/components/accordion.css +1 -1
- package/dist/skins-src/components/alert.css +3 -2
- package/dist/skins-src/components/autocomplete.css +1 -1
- package/dist/skins-src/components/avatar.css +1 -1
- package/dist/skins-src/components/badge.css +1 -1
- package/dist/skins-src/components/button.css +1 -1
- package/dist/skins-src/components/calendar.css +8 -6
- package/dist/skins-src/components/card.css +1 -1
- package/dist/skins-src/components/carousel.css +11 -1
- package/dist/skins-src/components/chat-composer.css +1 -1
- package/dist/skins-src/components/chat-message.css +1 -1
- package/dist/skins-src/components/checkbox-card.css +2 -2
- package/dist/skins-src/components/checkbox.css +1 -1
- package/dist/skins-src/components/clipboard.css +2 -2
- package/dist/skins-src/components/color-picker.css +4 -4
- package/dist/skins-src/components/combobox.css +2 -2
- package/dist/skins-src/components/command.css +1 -1
- package/dist/skins-src/components/dashboard-layout.css +21 -1
- package/dist/skins-src/components/date-field.css +2 -2
- package/dist/skins-src/components/dialog.css +4 -4
- package/dist/skins-src/components/drawer.css +9 -9
- package/dist/skins-src/components/editable.css +3 -3
- package/dist/skins-src/components/file-upload.css +4 -4
- package/dist/skins-src/components/floating-panel.css +1 -1
- package/dist/skins-src/components/forgot-password-page.css +1 -1
- package/dist/skins-src/components/form-section.css +1 -1
- package/dist/skins-src/components/form.css +12 -2
- package/dist/skins-src/components/image.css +1 -1
- package/dist/skins-src/components/kbd.css +1 -1
- package/dist/skins-src/components/link.css +1 -1
- package/dist/skins-src/components/listbox.css +1 -1
- package/dist/skins-src/components/mentions.css +1 -1
- package/dist/skins-src/components/menubar.css +2 -2
- package/dist/skins-src/components/navigation-menu.css +1 -1
- package/dist/skins-src/components/number-field.css +1 -1
- package/dist/skins-src/components/onboarding-checklist.css +1 -1
- package/dist/skins-src/components/pagination.css +2 -2
- package/dist/skins-src/components/password-input.css +80 -16
- package/dist/skins-src/components/pin-input.css +2 -2
- package/dist/skins-src/components/popover.css +1 -1
- package/dist/skins-src/components/qr-code.css +2 -2
- package/dist/skins-src/components/radio-card.css +1 -1
- package/dist/skins-src/components/reset-password-page.css +1 -1
- package/dist/skins-src/components/select.css +3 -3
- package/dist/skins-src/components/sign-in-page.css +1 -1
- package/dist/skins-src/components/sign-up-page.css +1 -1
- package/dist/skins-src/components/skeleton.css +1 -1
- package/dist/skins-src/components/skip-link.css +1 -1
- package/dist/skins-src/components/speed-dial.css +3 -3
- package/dist/skins-src/components/stat-card.css +6 -1
- package/dist/skins-src/components/stepper.css +3 -1
- package/dist/skins-src/components/switch.css +21 -1
- package/dist/skins-src/components/table.css +4 -4
- package/dist/skins-src/components/tags-input.css +2 -2
- package/dist/skins-src/components/text-field.css +26 -21
- package/dist/skins-src/components/text.css +1 -1
- package/dist/skins-src/components/textarea.css +1 -1
- package/dist/skins-src/components/time-field.css +2 -2
- package/dist/skins-src/components/toast.css +2 -2
- package/dist/skins-src/components/toggle-group.css +3 -3
- package/dist/skins-src/components/toggle.css +1 -1
- package/dist/skins-src/components/toolbar.css +2 -2
- package/dist/skins-src/components/tree-select.css +2 -2
- package/dist/skins-src/components/tree.css +7 -1
- package/dist/skins-src/components/usage-card.css +1 -1
- package/dist/skins-src/components/user-menu.css +1 -1
- package/dist/skins-src/components/verification-page.css +1 -1
- package/dist/skins-src/components/workspace-switcher.css +2 -2
- package/package.json +14 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
import{changeDetail as e}from"./change-reason.js";import{SELECTION_ATTRIBUTE_RULE as t,SELECTION_INPUT_RULE as n,SELECTION_OUTPUT_RULE as o}from"./selection-value.js";import{ROOT_DATA_ATTRIBUTES as a}from"./styling-hooks.js";var l={name:"Accordion",purpose:"A set of expandable sections. The root owns which item(s) are open and moves focus between triggers with the arrow keys, without taking any of them out of the page Tab sequence.",props:[{name:"type",type:"'single' | 'multiple'",required:!0,description:`"single" allows only one open item (optionally collapsible to none); "multiple" allows any number open. Required in all five packages, with no default: it is the discriminant the whole component's semantics turn on, so a forgotten value must fail loudly rather than silently picking one. React, Vue and Svelte make it the required discriminant of a prop union; Angular marks the input required and Web Components throws on connect, which is the only moment a custom element gets to refuse its own configuration.`},{name:"value",type:"string | string[]",required:!1,description:`Open item value(s), for controlled usage. ${n} ${o} ${t}`},{name:"defaultValue",type:"string | string[]",required:!1,description:`Initially open item value(s), for uncontrolled usage. Same shape as \`value\`. The collection form has to survive the markup path as well as the script path \u2014 an attribute is a string, and <skdx-accordion type="multiple" default-value="a,b"> was otherwise capable of seeding exactly one open item. ${n} ${t}`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Prevents every item from opening/closing."},{name:"orientation",type:"'horizontal' | 'vertical'",defaultValue:"'vertical'",required:!1,description:"Layout axis for arrow-key navigation between triggers."},{name:"collapsible",type:"boolean",defaultValue:"false",required:!1,description:'When type="single", allows closing the open item so none remain open.'},{name:"items",type:"ReadonlyArray<{ value: string; title: string; content?: string; disabled?: boolean }>",required:!1,description:"Item data the accordion renders itself from, instead of the AccordionItem/Header/Trigger/Content children a consumer writes by hand. `value` is the item's identity in `value`/`defaultValue`/`value-change`, exactly as the Item part's own `value` is; `title` is the trigger's text, `content` the panel body as plain text \u2014 `renderContent` is the escape hatch for a panel that needs markup \u2014 and `disabled` OR-s with the root's the same way the part's does. Composition stays the default and is what an accordion with rich headers should use; this is the source of truth the moment it is supplied. The rendered structure is identical to the hand-written one \u2014 one Item per entry, each with its Header, Trigger and Content \u2014 so every part, data attribute and keyboard behaviour is the same either way."},{name:"headingLevel",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Heading level for every derived AccordionHeader, h1 through h6. It exists because deriving the header takes the Header part's own `level` away from the consumer: without it an accordion rendered from `items` is locked to <h3> regardless of the surrounding document outline \u2014 the same skipped-level defect the Header part's own `level` prop fixes for the composed path. A heading level is a property of the document the accordion lands in, and only the page author knows it."},{name:"renderContent",type:"(item: { value: string; title: string; content?: string; disabled?: boolean }) => unknown",required:!1,frameworks:["react","svelte","angular","webComponents"],exception:"render-prop",frameworksReason:"Vue has no render-function member to name: it spells this as a `content` scoped slot receiving the same item. It is the same content hole expressed in each framework's own idiom \u2014 the gate matches member names literally and has no per-prop rename mechanism, which is how tree's `renderLabel` is already declared.",description:"Renders an item's panel body from its entry in `items`, for content that is more than the entry's `content` string. Ignored without `items`, since composition already puts the consumer in charge of the panel. **Property-only in Web Components**: a function has no attribute form, so <skdx-accordion> declares it `@property({ attribute: false })`. **implementation-adaptation:** Vue ships it as a `content` scoped slot receiving the same item \u2014 the same member in Vue's own idiom, not a missing one. Angular declares a literal `renderContent` @Input taking a TemplateRef, so it is named and matched like the other three."}],events:[{name:"value-change",description:`Fires whenever the open items change, controlled or not. ${o}`,detail:e("string[]","selectedValue: string | null")},{name:"value-change-detail",frameworks:["angular"],exception:"event-shape",frameworksReason:"Angular's `valueChange` emits the whole `string[]` selection so `[(value)]` round-trips it; this second output carries the detail object the other four hand to `value-change`.",description:"Fires with valueChange, carrying the open item values plus the item that changed.",detail:e("string[]","selectedValue: string | null")}],parts:[{name:"Item",description:"One expandable section. Owns its open/disabled state (both derived: open from the root's value, disabled from the root's OR its own) and hands it plus its generated ids to its Header, Trigger and Content.",props:[{name:"value",type:"string",required:!0,description:"Identifies this item. Matched against the root's open value(s), and reported back through value-change when this item opens or closes."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Prevents this item's trigger from opening or closing it. OR-ed with the root's disabled, never overridden \u2014 an item cannot re-enable itself inside a disabled accordion."},{name:"triggerId",type:"string",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Read-only on the custom element for the same reason getOpenValues is: the other four hand these generated ids down through context to the Header, Trigger and Content that need them, so a consumer never holds one. A Web Components consumer has no such channel \u2014 the parts are their own elements \u2014 and needs the id to point an external control at the trigger with aria-controls or to move focus to it.",description:"The generated id of this item's trigger button, readable once the element is upgraded. Use it to wire an external control's aria-controls, or to focus the trigger from code."},{name:"contentId",type:"string",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Same as triggerId: the other four pass it down through context to the parts that consume it, and only a Web Components consumer has to read it off the element.",description:"The generated id of this item's content panel, readable once the element is upgraded. Use it to wire an external control's aria-controls, or to scroll the panel into view."}]},{name:"Header",description:"The heading that wraps an item's trigger, giving the section a real <h1>\u2013<h6> in the accessibility tree. Mirrors its item's data-state and data-disabled.",props:[{name:"level",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Which heading element to render, h1 through h6. The default of 3 suits an accordion sitting under an h2 section heading, which is the common case, but it is only a default: a heading level is a property of the surrounding document outline, and only the page author knows it. Without this prop an accordion placed under an h4 emitted an h3 the consumer had no way to correct, skipping a level for every screen-reader user navigating by heading. Selects the rendered tag rather than writing aria-level onto a fixed one, so the outline is right for find-in-page, for the browser reading modes and for the accessibility tree alike."}]},{name:"Content",description:"The collapsible panel revealed when its item is open (role=region, labelled by its trigger). Unmounted while closed unless forceMount or hiddenUntilFound is set. Web Components are the exception: a part authored in markup is never unmounted by its element, so forceMount is not offered there \u2014 see the Tabs Content part for the same reasoning.",props:[{name:"forceMount",type:"boolean",defaultValue:"false",required:!1,frameworks:["react","vue","svelte","angular"],exception:"force-mount",frameworksReason:"Web Components never unmounts a closed overlay \u2014 the part hosts the consumer's own adopted light-DOM children and only hides them, so there is no unmount for forceMount to prevent and no exit animation it could rescue.",description:'Keep the panel mounted while closed, hidden outright via hidden="". For exit animations, or to preserve panel state across open/close. Not offered in Web Components, where parts never unmount.'},{name:"hiddenUntilFound",type:"boolean",defaultValue:"false",required:!1,description:'Use hidden="until-found" so find-in-page can search closed panel text; opens on beforematch to keep aria-expanded truthful. Implies forceMount \u2014 the two are not the same knob.'},{name:"role",type:"'region' | 'group'",defaultValue:"'region'",required:!1,description:'The landmark role the panel carries. An unconditional role="region" drops the condition APG attaches to it: a region is a landmark, and an accordion with more than about six panels floods the landmark list with entries that all read as the section title the user just navigated past. Pass "group" for a long accordion; aria-labelledby is required either way, so the panel is still named by its own trigger. In Web Components the canonical spelling is the native role attribute on <skdx-accordion-content>, which the element seeds with "region" and never overwrites \u2014 a custom element host carries the attribute itself, so a declared property would be a second source of truth for the same value.'}]},{name:"Indicator",description:`The disclosure affordance inside the trigger \u2014 a chevron or caret \u2014 as a <span> carrying data-state so the skin can rotate it. It exists because the skin already styled a .skdx-accordion__indicator and its [data-state="open"] rotation while no package emitted one: every consumer, and every e2e fixture in this repo, hand-wrote the class *and* mirrored the state onto it. It renders no glyph of its own \u2014 the icon is the consumer's child, since a package ships no artwork.`,props:[]}],slots:[{name:"default",description:"Root content \u2014 AccordionItems."},{name:"header",description:"An AccordionItem's AccordionHeader content \u2014 wraps the trigger."},{name:"trigger",description:"An AccordionItem's AccordionTrigger content \u2014 the toggle button."},{name:"content",description:"An AccordionItem's AccordionContent content \u2014 the revealed panel."},{name:"indicator",description:"An AccordionTrigger's AccordionIndicator content \u2014 the chevron or caret glyph."}],states:["open","closed"],controlledUncontrolled:"Supports both: `value` for controlled, `defaultValue` for uncontrolled. In Angular the pair is split: `valueChange` emits the whole `string[]` selection for `[(value)]`, `valueChangeDetail` carries the detail.",formBehavior:"Not applicable \u2014 Accordion does not participate in forms.",validationBehavior:"Not applicable \u2014 Accordion carries no value and participates in no validation.",accessibility:{role:"AccordionHeader is a real heading element, h3 by default and h1\u2013h6 via its level prop; AccordionContent is role=region by default, or role=group via its role prop",keyboard:[{key:"ArrowUp/ArrowDown (vertical) or ArrowLeft/ArrowRight (horizontal)",behavior:"Moves focus between triggers, wrapping at the ends."},{key:"Home",behavior:"Moves focus to the first trigger."},{key:"End",behavior:"Moves focus to the last trigger."}],ariaAttributes:["trigger's aria-controls points at its item's content id \u2014 emitted only while that content element is actually in the DOM. A closed panel is unmounted, which is the default configuration, so an unconditional aria-controls left every closed trigger pointing at an IDREF that resolves to nothing.","trigger's aria-expanded reflects the item's open state","content's aria-labelledby points back at its trigger's id",`aria-disabled="true" on the open trigger of a type="single" accordion that is not collapsible \u2014 activating it does nothing, and nothing else announces that. Never the native disabled attribute: the trigger keeps its place in the tab sequence, and disabling it natively would make the open section's own header the one header a keyboard user cannot reach.`],focusManagement:'Every enabled trigger is a tab stop; arrow keys move focus between triggers without moving the tab stop. A disabled trigger (a native <button disabled>, or tabindex="-1" plus aria-disabled on a host that cannot be one) is skipped by both Tab and the arrow keys.'},styling:{cssParts:["root","item","header","trigger","content","indicator"],dataAttributes:[...a,"data-state","data-disabled","data-orientation","data-skdx-accordion-trigger",{name:"data-skdx-accordion-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:[{name:"getOpenValues",signature:"() => string[]",description:"The currently open item values, resolving controlled `value` against the uncontrolled internal state. Web-Components-only: the item parts have no context to read the open set from.",frameworks:["webComponents"],exception:"imperative",frameworksReason:"React, Vue, Svelte and Angular publish the open set on their context value, not through the root: all three of the first carry it as `value` on the object the Accordion provides, and Angular's items inject SkdxAccordionState and read its `openValues` signal. None of the four hands a consumer a root instance to call a method on, so there is no element or instance for this to hang off."},{name:"isOpen",signature:"(value: string) => boolean",description:"Whether `value` is one of the open items. Web-Components-only, for the same reason as getOpenValues.",frameworks:["webComponents"],exception:"imperative",frameworksReason:"Same as getOpenValues: the other four answer this from context. Angular comes closest and still is not it \u2014 its `isOpen()` is a computed on SkdxAccordionItemState, i.e. one item answering about itself, not the root answering about a value."},{name:"toggleItem",signature:"(value: string) => void",description:"Opens or closes `value`, honouring type/collapsible/disabled, and fires value-change. Web-Components-only: it is how a trigger, which cannot reach a context callback, tells its root that it was activated.",frameworks:["webComponents"],exception:"imperative",frameworksReason:"The other four give their triggers a context callback pair instead \u2014 `onItemOpen`/`onItemClose`, spelled identically on React's AccordionContextValue, Vue's ACCORDION_CONTEXT, Svelte's AccordionContextValue and Angular's SkdxAccordionState \u2014 so the state change travels down the tree it belongs to rather than out through a public imperative handle a consumer could also call and desynchronise."}]};export{l as accordionContract};
|
|
1
|
+
import{changeDetail as e}from"./change-reason.js";import{SELECTION_ATTRIBUTE_RULE as t,SELECTION_INPUT_RULE as n,SELECTION_OUTPUT_RULE as o}from"./selection-value.js";import{ROOT_DATA_ATTRIBUTES as a}from"./styling-hooks.js";var l={name:"Accordion",purpose:"A set of expandable sections. The root owns which item(s) are open and moves focus between triggers with the arrow keys, without taking any of them out of the page Tab sequence.",props:[{name:"type",type:"'single' | 'multiple'",required:!0,description:`"single" allows only one open item (optionally collapsible to none); "multiple" allows any number open. Required in all five packages, with no default: it is the discriminant the whole component's semantics turn on, so a forgotten value must fail loudly rather than silently picking one. React, Vue and Svelte make it the required discriminant of a prop union; Angular marks the input required and Web Components throws on connect, which is the only moment a custom element gets to refuse its own configuration.`},{name:"value",type:"string | string[]",required:!1,description:`Open item value(s), for controlled usage. ${n} ${o} ${t}`},{name:"defaultValue",type:"string | string[]",required:!1,description:`Initially open item value(s), for uncontrolled usage. Same shape as \`value\`. The collection form has to survive the markup path as well as the script path \u2014 an attribute is a string, and <skdx-accordion type="multiple" default-value="a,b"> was otherwise capable of seeding exactly one open item. ${n} ${t}`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Prevents every item from opening/closing."},{name:"orientation",type:"'horizontal' | 'vertical'",defaultValue:"'vertical'",required:!1,description:"Layout axis for arrow-key navigation between triggers."},{name:"collapsible",type:"boolean",defaultValue:"false",required:!1,description:'When type="single", allows closing the open item so none remain open.'},{name:"items",type:"ReadonlyArray<{ value: string; title: string; content?: string; disabled?: boolean }>",required:!1,description:"Item data the accordion renders itself from, instead of the AccordionItem/Header/Trigger/Content children a consumer writes by hand. `value` is the item's identity in `value`/`defaultValue`/`value-change`, exactly as the Item part's own `value` is; `title` is the trigger's text, `content` the panel body as plain text \u2014 `renderContent` is the escape hatch for a panel that needs markup \u2014 and `disabled` OR-s with the root's the same way the part's does. Composition stays the default and is what an accordion with rich headers should use; this is the source of truth the moment it is supplied. The rendered structure is identical to the hand-written one \u2014 one Item per entry, each with its Header, Trigger and Content \u2014 so every part, data attribute and keyboard behaviour is the same either way."},{name:"headingLevel",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Heading level for every derived AccordionHeader, h1 through h6. It exists because deriving the header takes the Header part's own `level` away from the consumer: without it an accordion rendered from `items` is locked to <h3> regardless of the surrounding document outline \u2014 the same skipped-level defect the Header part's own `level` prop fixes for the composed path. A heading level is a property of the document the accordion lands in, and only the page author knows it."},{name:"renderContent",type:"(item: { value: string; title: string; content?: string; disabled?: boolean }) => unknown",required:!1,frameworks:["react","svelte","angular","webComponents"],exception:"render-prop",frameworksReason:"Vue has no render-function member to name: it spells this as a `content` scoped slot receiving the same item. It is the same content hole expressed in each framework's own idiom \u2014 the gate matches member names literally and has no per-prop rename mechanism, which is how tree's `renderLabel` is already declared.",description:"Renders an item's panel body from its entry in `items`, for content that is more than the entry's `content` string. Ignored without `items`, since composition already puts the consumer in charge of the panel. **Property-only in Web Components**: a function has no attribute form, so <skdx-accordion> declares it `@property({ attribute: false })`. **implementation-adaptation:** Vue ships it as a `content` scoped slot receiving the same item \u2014 the same member in Vue's own idiom, not a missing one. Angular declares a literal `renderContent` @Input taking a TemplateRef, so it is named and matched like the other three."}],events:[{name:"value-change",description:`Fires whenever the open items change, controlled or not. ${o}`,detail:e("string[]","selectedValue: string | null")},{name:"value-change-detail",frameworks:["angular"],exception:"event-shape",frameworksReason:"Angular's `valueChange` emits the whole `string[]` selection so `[(value)]` round-trips it; this second output carries the detail object the other four hand to `value-change`.",description:"Fires with valueChange, carrying the open item values plus the item that changed.",detail:e("string[]","selectedValue: string | null")}],parts:[{name:"Item",description:"One expandable section. Owns its open/disabled state (both derived: open from the root's value, disabled from the root's OR its own) and hands it plus its generated ids to its Header, Trigger and Content.",props:[{name:"value",type:"string",required:!0,description:"Identifies this item. Matched against the root's open value(s), and reported back through value-change when this item opens or closes."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Prevents this item's trigger from opening or closing it. OR-ed with the root's disabled, never overridden \u2014 an item cannot re-enable itself inside a disabled accordion."},{name:"triggerId",type:"string",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Read-only on the custom element for the same reason getOpenValues is: the other four hand these generated ids down through context to the Header, Trigger and Content that need them, so a consumer never holds one. A Web Components consumer has no such channel \u2014 the parts are their own elements \u2014 and needs the id to point an external control at the trigger with aria-controls or to move focus to it.",description:"The generated id of this item's trigger button, readable once the element is upgraded. Use it to wire an external control's aria-controls, or to focus the trigger from code."},{name:"contentId",type:"string",required:!1,frameworks:["webComponents"],exception:"wc-internals",frameworksReason:"Same as triggerId: the other four pass it down through context to the parts that consume it, and only a Web Components consumer has to read it off the element.",description:"The generated id of this item's content panel, readable once the element is upgraded. Use it to wire an external control's aria-controls, or to scroll the panel into view."}]},{name:"Header",description:"The heading that wraps an item's trigger, giving the section a real <h1>\u2013<h6> in the accessibility tree. Mirrors its item's data-state and data-disabled.",props:[{name:"level",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Which heading element to render, h1 through h6. The default of 3 suits an accordion sitting under an h2 section heading, which is the common case, but it is only a default: a heading level is a property of the surrounding document outline, and only the page author knows it. Without this prop an accordion placed under an h4 emitted an h3 the consumer had no way to correct, skipping a level for every screen-reader user navigating by heading. Selects the rendered tag rather than writing aria-level onto a fixed one, so the outline is right for find-in-page, for the browser reading modes and for the accessibility tree alike."}],slots:[{name:"default",description:"An AccordionItem's AccordionHeader content \u2014 wraps the trigger."}]},{name:"Content",description:"The collapsible panel revealed when its item is open (role=region, labelled by its trigger). Unmounted while closed unless forceMount or hiddenUntilFound is set. Web Components are the exception: a part authored in markup is never unmounted by its element, so forceMount is not offered there \u2014 see the Tabs Content part for the same reasoning.",props:[{name:"forceMount",type:"boolean",defaultValue:"false",required:!1,frameworks:["react","vue","svelte","angular"],exception:"force-mount",frameworksReason:"Web Components never unmounts a closed overlay \u2014 the part hosts the consumer's own adopted light-DOM children and only hides them, so there is no unmount for forceMount to prevent and no exit animation it could rescue.",description:'Keep the panel mounted while closed, hidden outright via hidden="". For exit animations, or to preserve panel state across open/close. Not offered in Web Components, where parts never unmount.'},{name:"hiddenUntilFound",type:"boolean",defaultValue:"false",required:!1,description:'Use hidden="until-found" so find-in-page can search closed panel text; opens on beforematch to keep aria-expanded truthful. Implies forceMount \u2014 the two are not the same knob.'},{name:"role",type:"'region' | 'group'",defaultValue:"'region'",required:!1,description:'The landmark role the panel carries. An unconditional role="region" drops the condition APG attaches to it: a region is a landmark, and an accordion with more than about six panels floods the landmark list with entries that all read as the section title the user just navigated past. Pass "group" for a long accordion; aria-labelledby is required either way, so the panel is still named by its own trigger. In Web Components the canonical spelling is the native role attribute on <skdx-accordion-content>, which the element seeds with "region" and never overwrites \u2014 a custom element host carries the attribute itself, so a declared property would be a second source of truth for the same value.'}],slots:[{name:"default",description:"An AccordionItem's AccordionContent content \u2014 the revealed panel."}]},{name:"Indicator",description:`The disclosure affordance inside the trigger \u2014 a chevron or caret \u2014 as a <span> carrying data-state so the skin can rotate it. It exists because the skin already styled a .skdx-accordion__indicator and its [data-state="open"] rotation while no package emitted one: every consumer, and every e2e fixture in this repo, hand-wrote the class *and* mirrored the state onto it. It renders no glyph of its own \u2014 the icon is the consumer's child, since a package ships no artwork.`,props:[],slots:[{name:"default",description:"An AccordionTrigger's AccordionIndicator content \u2014 the chevron or caret glyph."}]},{name:"Trigger",description:"The native button that toggles its AccordionItem and participates in accordion keyboard navigation.",slots:[{name:"default",description:"An AccordionItem's AccordionTrigger content \u2014 the toggle button."}]}],slots:[{name:"default",description:"Root content \u2014 AccordionItems."},{name:"content",description:"An AccordionItem's AccordionContent content \u2014 the revealed panel.",frameworks:["vue"],exception:"derived-anatomy",frameworksReason:"The root exposes content as content projection in vue. Other wrappers project default content into the Content part or use their separately documented rendering property; they do not accept a root named slot with this spelling."},{name:"render-content",frameworks:["svelte"],exception:"render-prop",frameworksReason:"svelte expose render-content as native content projection for the renderContent rendering capability. Other frameworks pass the documented renderContent render prop or template instead of a named slot with this spelling.",description:"Renders an item's panel body from its entry in `items`, for content that is more than the entry's `content` string. Ignored without `items`, since composition already puts the consumer in charge of the panel. **Property-only in Web Components**: a function has no attribute form, so <skdx-accordion> declares it `@property({ attribute: false })`. **implementation-adaptation:** Vue ships it as a `content` scoped slot receiving the same item \u2014 the same member in Vue's own idiom, not a missing one. Angular declares a literal `renderContent` @Input taking a TemplateRef, so it is named and matched like the other three."}],states:["open","closed"],controlledUncontrolled:"Supports both: `value` for controlled, `defaultValue` for uncontrolled. In Angular the pair is split: `valueChange` emits the whole `string[]` selection for `[(value)]`, `valueChangeDetail` carries the detail.",formBehavior:"Not applicable \u2014 Accordion does not participate in forms.",validationBehavior:"Not applicable \u2014 Accordion carries no value and participates in no validation.",accessibility:{role:"AccordionHeader is a real heading element, h3 by default and h1\u2013h6 via its level prop; AccordionContent is role=region by default, or role=group via its role prop",keyboard:[{key:"ArrowUp/ArrowDown (vertical) or ArrowLeft/ArrowRight (horizontal)",behavior:"Moves focus between triggers, wrapping at the ends."},{key:"Home",behavior:"Moves focus to the first trigger."},{key:"End",behavior:"Moves focus to the last trigger."}],ariaAttributes:["trigger's aria-controls points at its item's content id \u2014 emitted only while that content element is actually in the DOM. A closed panel is unmounted, which is the default configuration, so an unconditional aria-controls left every closed trigger pointing at an IDREF that resolves to nothing.","trigger's aria-expanded reflects the item's open state","content's aria-labelledby points back at its trigger's id",`aria-disabled="true" on the open trigger of a type="single" accordion that is not collapsible \u2014 activating it does nothing, and nothing else announces that. Never the native disabled attribute: the trigger keeps its place in the tab sequence, and disabling it natively would make the open section's own header the one header a keyboard user cannot reach.`],focusManagement:'Every enabled trigger is a tab stop; arrow keys move focus between triggers without moving the tab stop. A disabled trigger (a native <button disabled>, or tabindex="-1" plus aria-disabled on a host that cannot be one) is skipped by both Tab and the arrow keys.'},styling:{cssParts:["root","item","header","trigger","content","indicator"],dataAttributes:[...a,"data-state","data-disabled","data-orientation","data-skdx-accordion-trigger",{name:"data-skdx-accordion-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:[{name:"getOpenValues",signature:"() => string[]",description:"The currently open item values, resolving controlled `value` against the uncontrolled internal state. Web-Components-only: the item parts have no context to read the open set from.",frameworks:["webComponents"],exception:"imperative",frameworksReason:"React, Vue, Svelte and Angular publish the open set on their context value, not through the root: all three of the first carry it as `value` on the object the Accordion provides, and Angular's items inject SkdxAccordionState and read its `openValues` signal. None of the four hands a consumer a root instance to call a method on, so there is no element or instance for this to hang off."},{name:"isOpen",signature:"(value: string) => boolean",description:"Whether `value` is one of the open items. Web-Components-only, for the same reason as getOpenValues.",frameworks:["webComponents"],exception:"imperative",frameworksReason:"Same as getOpenValues: the other four answer this from context. Angular comes closest and still is not it \u2014 its `isOpen()` is a computed on SkdxAccordionItemState, i.e. one item answering about itself, not the root answering about a value."},{name:"toggleItem",signature:"(value: string) => void",description:"Opens or closes `value`, honouring type/collapsible/disabled, and fires value-change. Web-Components-only: it is how a trigger, which cannot reach a context callback, tells its root that it was activated.",frameworks:["webComponents"],exception:"imperative",frameworksReason:"The other four give their triggers a context callback pair instead \u2014 `onItemOpen`/`onItemClose`, spelled identically on React's AccordionContextValue, Vue's ACCORDION_CONTEXT, Svelte's AccordionContextValue and Angular's SkdxAccordionState \u2014 so the state change travels down the tree it belongs to rather than out through a public imperative handle a consumer could also call and desynchronise."}]};export{l as accordionContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var i={name:"Alert",purpose:"An inline live region for surfacing important information, distinct from Toast (a floating, auto-dismissing notification). Composed of the Alert root plus AlertTitle, AlertDescription, AlertIcon, AlertClose, and AlertAction \u2014 where the root links to the title/description ids via aria-labelledby/aria-describedby. Never auto-dismisses: the only way it goes away is the consumer or AlertClose setting open to false.",props:[{name:"role",type:"'status' | 'alert'",defaultValue:"'status'",required:!1,description:'"status" is a polite live region for informational banners; "alert" is assertive, for time-critical/error messages. Also sets aria-live accordingly.'},{name:"open",type:"boolean",required:!1,description:"Controlled visibility. Pair with the open-change event; when defined, the Alert renders whatever this says and never dismisses itself."},{name:"defaultOpen",type:"boolean",defaultValue:"true",required:!1,description:"Initial visibility, for uncontrolled usage. Defaults to true so a plain Alert renders exactly as it did before dismissal existed. Absent in Web Components, which has no controlled/uncontrolled split \u2014 see controlledUncontrolled.",frameworks:["react","vue","svelte","angular"],exception:"wc-default",frameworksReason:'<skdx-alert> declares one reflected `open` property with an optOutBoolean converter, which is both the initial value and the current value, exactly as a native <input>\'s `checked` is. An attribute-driven model has no second slot for "initial only", and adding one would mean an attribute that stops tracking its property after first render.'},{name:"closeLabel",type:"string",defaultValue:"'Dismiss'",required:!1,description:`Accessible name for the Close part, which is icon-only in every documented example and so has no text of its own to be named by. It lives on the root rather than on Close for the same reason the Clipboard root's copiedLabel does: it is the one string this family invents, an application sets it once alongside the rest of its copy, and the part that consumes it is often written by a different hand than the one that configures the family. Without it the button shipped the hard-coded English string "Dismiss" in all five packages, with no prop to change it. An aria-label written on Close itself still wins.`},{name:"dismissOnEscape",type:"boolean",defaultValue:"false",required:!1,description:'Whether Escape dismisses the alert while focus is inside it. Opt-in, and false by default, because an Escape handler that removes the focused element and drops focus on document.body is worse than no handler at all \u2014 it is only safe paired with the focus restoration the root now performs on every dismissal. One reference of four ships it (Spectrum: "Esc \u2014 Dismiss a dismissible alert"), against an APG pattern whose keyboard section reads "Not applicable", which is why it is not the default.'},{name:"title",type:"string",required:!1,description:"The alert's title as a plain string, which the recipe renders into a derived Title part \u2014 so the ordinary banner is one element rather than five. Composing AlertTitle by hand is still the path for rich markup, and it is the same part reached the other way; the root's aria-labelledby points at it either way."},{name:"description",type:"string",required:!1,description:"The alert's supporting text as a plain string, which the recipe renders into a derived Description part. Stands in the same relation to a hand-composed AlertDescription as `title` does to AlertTitle, and is what aria-describedby points at."},{name:"dismissible",type:"boolean",defaultValue:"false",required:!1,description:'Renders a Close control. In Web Components a `slot="close"` child still wins for the glyph.'},{name:"status",type:"'info' | 'success' | 'warning' | 'danger' | (string & {})",required:!1,description:"Severity rung, written to data-status on the root; the data-status attribute may be set directly instead."},{name:"variant",type:"'solid' | 'outline' | (string & {})",required:!1,description:"Fill style rung, written to data-variant on the root; the data-variant attribute may be set directly instead."}],events:[{name:"open-change",description:"Fired with the new visibility whenever it changes \u2014 in practice, false when AlertClose is activated. Fires in both controlled and uncontrolled modes.",detail:"boolean"}],parts:[{name:"Title",description:"The alert's accessible name; the root points aria-labelledby at its generated id."},{name:"Description",description:"The alert's supporting text; the root points aria-describedby at its generated id."},{name:"Icon",description:"A purely decorative icon slot, always aria-hidden."},{name:"Close",description:'A type="button" dismiss control inside the alert. Activating it sets open to false (uncontrolled) and fires open-change with false. Carries a default accessible name ("Dismiss") so an icon-only close button is never unnamed; override it with aria-label when the wording should differ.',props:[{name:"label",type:"string",required:!1,frameworks:["webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Angular take `aria-label` on the element through attribute passthrough.",description:"Accessible name for the dismiss button, overriding the root `closeLabel`."}]},{name:"Action",description:`A container for the alert's buttons and links ("Retry", "View details"). Purely a composition/styling slot \u2014 it renders a plain element with no role, no behavior, and no opinion about what goes inside.`}],slots:[{name:"default",description:"Alert body \u2014 typically AlertIcon, AlertTitle, AlertDescription, AlertAction, and AlertClose."},{name:"title",description:"AlertTitle's content \u2014 becomes the root's accessible name via aria-labelledby."},{name:"description",description:"AlertDescription's content \u2014 becomes the root's accessible description via aria-describedby."},{name:"icon",description:"AlertIcon's content \u2014 always aria-hidden, purely decorative."},{name:"close",description:"AlertClose's content \u2014 the dismiss control's label or icon."},{name:"action",description:"AlertAction's content \u2014 buttons and links responding to the alert."}],appearance:[{attribute:"data-status",values:["info","success","warning","danger"],stylesheet:"_status.css"},{attribute:"data-variant",values:["solid","outline"],stylesheet:"alert.css"}],states:["polite","assertive"],controlledUncontrolled:"Uncontrolled by default: starts at defaultOpen (true) and owns its own visibility, so AlertClose dismisses without any consumer wiring. Passing open switches to controlled \u2014 the consumer owns visibility and must update it from open-change, or AlertClose will fire the event and change nothing. Same model as Dialog, only with the default flipped to open. Alert never dismisses on a timer: no auto-timeout exists at any layer. Web Components has no defaultOpen, and the generated framework mapping's blank for it is a documented architecture difference rather than a missing property: <skdx-alert> declares one reflected `open` property with an optOutBoolean converter, which is both the initial value and the current value, exactly as a native <input>'s `checked` is. An attribute-driven model has no second slot for \"initial only\", and adding one would mean an attribute that stops tracking its property after first render \u2014 the same reason Dialog's ports diverge here.",formBehavior:"Not applicable \u2014 Alert participates in no form.",validationBehavior:"Not applicable \u2014 Alert carries no value and participates in no validation.",accessibility:{role:'status (aria-live="polite") or alert (aria-live="assertive"), chosen by the role prop',keyboard:[{key:"Enter / Space",behavior:"Activates AlertClose, dismissing the alert \u2014 native <button> behavior, not a custom key handler."},{key:"Escape",behavior:"Dismisses the alert when dismissOnEscape is set and focus is inside it. Does nothing otherwise, which is the default."}],ariaAttributes:["aria-live reflects the role prop","aria-labelledby points at AlertTitle's generated id","aria-describedby points at AlertDescription's generated id",`aria-label on AlertClose, defaulting to the root's closeLabel ("Dismiss")`],focusManagement:"Does not move focus while open \u2014 a live region only announces, without knocking users out of context. Dismissing does move focus, and must: the Close button is inside the alert, so removing the alert destroys the element that has focus and it falls to document.body, which loses a keyboard user their place in the page. On dismissal focus goes to the nearest focusable element before the alert, or failing that back to whatever held focus when the alert mounted; if neither exists nothing is moved. Focus is moved before the alert leaves the document, while its previous sibling can still be found from it. A dismissed Alert must also be absent from the accessibility tree \u2014 unmounting it and hiding it are both acceptable implementations of that, and the packages do both.",namingRequirement:`AlertTitle supplies the accessible name; render it whenever the Alert is present. AlertClose carries its own accessible name (the root's closeLabel, default "Dismiss") independently of the title. Severity is not a name and must not be carried by colour alone: an Alert with a non-neutral data-status must also render an AlertIcon or name the severity in its title, because the wash is the only other signal and an alert distinguished only by a wash is invisible to a reader who cannot see it.`},styling:{cssParts:["alert","title","description","icon","close","action"],dataAttributes:[...e,"data-status","data-variant",{name:"data-skdx-alert-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:[{name:"setOpen",signature:"setOpen(next: boolean): void",frameworks:["webComponents"],exception:"imperative",frameworksReason:"The other four frameworks express the same operation as a prop assignment (`open`) plus their own change callback, and have no element instance to hang a method off. `<skdx-alert>` needs it because its `open` is a plain reflected property with no controlled/uncontrolled split, so `setOpen` is what gives an assignment its `open-change` event \u2014 and because `<skdx-alert-close>` has to reach it across a shadow boundary.",description:"Sets open and dispatches the bubbling, composed open-change event with the new value as detail; setting the state it is already in is a no-op and dispatches nothing."}]};export{i as alertContract};
|
|
1
|
+
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var n={name:"Alert",purpose:"An inline live region for surfacing important information, distinct from Toast (a floating, auto-dismissing notification). Composed of the Alert root plus AlertTitle, AlertDescription, AlertIcon, AlertClose, and AlertAction \u2014 where the root links to the title/description ids via aria-labelledby/aria-describedby. Never auto-dismisses: the only way it goes away is the consumer or AlertClose setting open to false.",props:[{name:"role",type:"'status' | 'alert'",defaultValue:"'status'",required:!1,description:'"status" is a polite live region for informational banners; "alert" is assertive, for time-critical/error messages. Also sets aria-live accordingly.'},{name:"open",type:"boolean",required:!1,description:"Controlled visibility. Pair with the open-change event; when defined, the Alert renders whatever this says and never dismisses itself."},{name:"defaultOpen",type:"boolean",defaultValue:"true",required:!1,description:"Initial visibility, for uncontrolled usage. Defaults to true so a plain Alert renders exactly as it did before dismissal existed. Absent in Web Components, which has no controlled/uncontrolled split \u2014 see controlledUncontrolled.",frameworks:["react","vue","svelte","angular"],exception:"wc-default",frameworksReason:'<skdx-alert> declares one reflected `open` property with an optOutBoolean converter, which is both the initial value and the current value, exactly as a native <input>\'s `checked` is. An attribute-driven model has no second slot for "initial only", and adding one would mean an attribute that stops tracking its property after first render.'},{name:"closeLabel",type:"string",defaultValue:"'Dismiss'",required:!1,description:`Accessible name for the Close part, which is icon-only in every documented example and so has no text of its own to be named by. It lives on the root rather than on Close for the same reason the Clipboard root's copiedLabel does: it is the one string this family invents, an application sets it once alongside the rest of its copy, and the part that consumes it is often written by a different hand than the one that configures the family. Without it the button shipped the hard-coded English string "Dismiss" in all five packages, with no prop to change it. An aria-label written on Close itself still wins.`},{name:"dismissOnEscape",type:"boolean",defaultValue:"false",required:!1,description:'Whether Escape dismisses the alert while focus is inside it. Opt-in, and false by default, because an Escape handler that removes the focused element and drops focus on document.body is worse than no handler at all \u2014 it is only safe paired with the focus restoration the root now performs on every dismissal. One reference of four ships it (Spectrum: "Esc \u2014 Dismiss a dismissible alert"), against an APG pattern whose keyboard section reads "Not applicable", which is why it is not the default.'},{name:"title",type:"string",required:!1,description:"The alert's title as a plain string, which the recipe renders into a derived Title part \u2014 so the ordinary banner is one element rather than five. Composing AlertTitle by hand is still the path for rich markup, and it is the same part reached the other way; the root's aria-labelledby points at it either way."},{name:"description",type:"string",required:!1,description:"The alert's supporting text as a plain string, which the recipe renders into a derived Description part. Stands in the same relation to a hand-composed AlertDescription as `title` does to AlertTitle, and is what aria-describedby points at."},{name:"dismissible",type:"boolean",defaultValue:"false",required:!1,description:'Renders a Close control. In Web Components a `slot="close"` child still wins for the glyph.'},{name:"status",type:"'info' | 'success' | 'warning' | 'danger' | (string & {})",required:!1,description:"Severity rung, written to data-status on the root; the data-status attribute may be set directly instead."},{name:"variant",type:"'solid' | 'outline' | (string & {})",required:!1,description:"Fill style rung, written to data-variant on the root; the data-variant attribute may be set directly instead."}],events:[{name:"open-change",description:"Fired with the new visibility whenever it changes \u2014 in practice, false when AlertClose is activated. Fires in both controlled and uncontrolled modes.",detail:"boolean"}],parts:[{name:"Title",description:"The alert's accessible name; the root points aria-labelledby at its generated id.",slots:[{name:"default",description:"AlertTitle's content \u2014 becomes the root's accessible name via aria-labelledby."}]},{name:"Description",description:"The alert's supporting text; the root points aria-describedby at its generated id.",slots:[{name:"default",description:"AlertDescription's content \u2014 becomes the root's accessible description via aria-describedby."}]},{name:"Icon",description:"A purely decorative icon slot, always aria-hidden."},{name:"Close",description:'A type="button" dismiss control inside the alert. Activating it sets open to false (uncontrolled) and fires open-change with false. Carries a default accessible name ("Dismiss") so an icon-only close button is never unnamed; override it with aria-label when the wording should differ.',props:[{name:"label",type:"string",required:!1,frameworks:["webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue, Svelte and Angular take `aria-label` on the element through attribute passthrough.",description:"Accessible name for the dismiss button, overriding the root `closeLabel`."}],slots:[{name:"default",description:"AlertClose's content \u2014 the dismiss control's label or icon."}]},{name:"Action",description:`A container for the alert's buttons and links ("Retry", "View details"). Purely a composition/styling slot \u2014 it renders a plain element with no role, no behavior, and no opinion about what goes inside.`}],slots:[{name:"close",frameworks:["webComponents"],exception:"derived-anatomy",frameworksReason:'The Web Components root consumes slot="close" through claimSlot to customize its generated close control. Other frameworks compose AlertClose and project its default content.',description:"Custom content for the generated AlertClose control."},{name:"default",description:"Alert body \u2014 typically AlertIcon, AlertTitle, AlertDescription, AlertAction, and AlertClose."},{name:"description",description:"AlertDescription's content \u2014 becomes the root's accessible description via aria-describedby.",frameworks:["react"],exception:"derived-anatomy",frameworksReason:"The root exposes description as content projection in react. Other wrappers project default content into the Description part or use their separately documented rendering property; they do not accept a root named slot with this spelling."},{name:"icon",description:"AlertIcon's content \u2014 always aria-hidden, purely decorative."},{name:"action",description:"AlertAction's content \u2014 buttons and links responding to the alert."}],appearance:[{attribute:"data-status",values:["info","success","warning","danger"],stylesheet:"_status.css"},{attribute:"data-variant",values:["solid","outline"],stylesheet:"alert.css"}],states:["polite","assertive"],controlledUncontrolled:"Uncontrolled by default: starts at defaultOpen (true) and owns its own visibility, so AlertClose dismisses without any consumer wiring. Passing open switches to controlled \u2014 the consumer owns visibility and must update it from open-change, or AlertClose will fire the event and change nothing. Same model as Dialog, only with the default flipped to open. Alert never dismisses on a timer: no auto-timeout exists at any layer. Web Components has no defaultOpen, and the generated framework mapping's blank for it is a documented architecture difference rather than a missing property: <skdx-alert> declares one reflected `open` property with an optOutBoolean converter, which is both the initial value and the current value, exactly as a native <input>'s `checked` is. An attribute-driven model has no second slot for \"initial only\", and adding one would mean an attribute that stops tracking its property after first render \u2014 the same reason Dialog's ports diverge here.",formBehavior:"Not applicable \u2014 Alert participates in no form.",validationBehavior:"Not applicable \u2014 Alert carries no value and participates in no validation.",accessibility:{role:'status (aria-live="polite") or alert (aria-live="assertive"), chosen by the role prop',keyboard:[{key:"Enter / Space",behavior:"Activates AlertClose, dismissing the alert \u2014 native <button> behavior, not a custom key handler."},{key:"Escape",behavior:"Dismisses the alert when dismissOnEscape is set and focus is inside it. Does nothing otherwise, which is the default."}],ariaAttributes:["aria-live reflects the role prop","aria-labelledby points at AlertTitle's generated id","aria-describedby points at AlertDescription's generated id",`aria-label on AlertClose, defaulting to the root's closeLabel ("Dismiss")`],focusManagement:"Does not move focus while open \u2014 a live region only announces, without knocking users out of context. Dismissing does move focus, and must: the Close button is inside the alert, so removing the alert destroys the element that has focus and it falls to document.body, which loses a keyboard user their place in the page. On dismissal focus goes to the nearest focusable element before the alert, or failing that back to whatever held focus when the alert mounted; if neither exists nothing is moved. Focus is moved before the alert leaves the document, while its previous sibling can still be found from it. A dismissed Alert must also be absent from the accessibility tree \u2014 unmounting it and hiding it are both acceptable implementations of that, and the packages do both.",namingRequirement:`AlertTitle supplies the accessible name; render it whenever the Alert is present. AlertClose carries its own accessible name (the root's closeLabel, default "Dismiss") independently of the title. Severity is not a name and must not be carried by colour alone: an Alert with a non-neutral data-status must also render an AlertIcon or name the severity in its title, because the wash is the only other signal and an alert distinguished only by a wash is invisible to a reader who cannot see it.`},styling:{cssParts:["alert","title","description","icon","close","action"],dataAttributes:[...e,"data-status","data-variant",{name:"data-skdx-alert-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:[{name:"setOpen",signature:"setOpen(next: boolean): void",frameworks:["webComponents"],exception:"imperative",frameworksReason:"The other four frameworks express the same operation as a prop assignment (`open`) plus their own change callback, and have no element instance to hang a method off. `<skdx-alert>` needs it because its `open` is a plain reflected property with no controlled/uncontrolled split, so `setOpen` is what gives an assignment its `open-change` event \u2014 and because `<skdx-alert-close>` has to reach it across a shadow boundary.",description:"Sets open and dispatches the bubbling, composed open-change event with the new value as detail; setting the state it is already in is a no-op and dispatches nothing."}],frameworkExtensions:[{framework:"react",name:"action",type:"React.ReactNode",description:'Buttons and links responding to the alert ("Retry", "View details"),\nrendered into the derived `AlertAction` part after the description. 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:"action",type:"Snippet | undefined",description:"The alert's action controls \u2014 buttons and links \u2014 rendered inside the\nderived `AlertAction` row. 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:"action",type:"TemplateRef<unknown>",description:"Action controls \u2014 buttons and links \u2014 rendered inside `<skdx-alert-action>`, bound as a `TemplateRef`. 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:"alertRole",type:"'status' | 'alert'",description:'`"status"` is a polite live region for informational banners; `"alert"` is for assertive, time-critical messages. 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:"react",name:"icon",type:"React.ReactNode",description:"Decorative glyph rendered into the derived `AlertIcon` part, always `aria-hidden`.\nConsumer supplies content; position is derived. 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:"icon",type:"Snippet | undefined",description:"Decorative icon content, rendered inside the derived `AlertIcon`, always `aria-hidden`. 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:"icon",type:"TemplateRef<unknown>",description:"Decorative icon, rendered inside the always-`aria-hidden` `<skdx-alert-icon>`, bound as a `TemplateRef`. 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:"react",name:"action",type:"React.ReactNode",description:'Buttons and links responding to the alert ("Retry", "View details"),\nrendered into the derived `AlertAction` part after the description. 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:"action",type:"Snippet | undefined",description:"The alert's action controls \u2014 buttons and links \u2014 rendered inside the\nderived `AlertAction` row. 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:"action",type:"TemplateRef<unknown>",description:"Action controls \u2014 buttons and links \u2014 rendered inside `<skdx-alert-action>`, bound as a `TemplateRef`. 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:"webComponents",name:"alertRole",type:"'status' | 'alert'",description:'`"status"` is a polite live region for informational banners; `"alert"` is for assertive, time-critical messages. 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.'},{framework:"react",name:"icon",type:"React.ReactNode",description:"Decorative glyph rendered into the derived `AlertIcon` part, always `aria-hidden`.\nConsumer supplies content; position is derived. 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:"icon",type:"Snippet | undefined",description:"Decorative icon content, rendered inside the derived `AlertIcon`, always `aria-hidden`. 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:"icon",type:"TemplateRef<unknown>",description:"Decorative icon, rendered inside the always-`aria-hidden` `<skdx-alert-icon>`, bound as a `TemplateRef`. 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."}]};export{n as alertContract};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComponentContract } from './types.js';
|
|
2
2
|
/** Contract for a free-text field with a suggestion popup, using virtual focus over a listbox and not constraining input to the list. */
|
|
3
3
|
export declare const autocompleteContract: ComponentContract;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{changeDescription as a,changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as n,setCustomValidityMethod as o}from"./form-validity.js";import{dismissalEvents as i,forceMountProp as t,openChangeCompleteEvent as s,popperContentProps as r,popperMeasurementProperties as l,portalPart as d,portalPropsSansForceMount as h}from"./overlay-parts.js";import{PART_DATA_ATTRIBUTES as u}from"./styling-hooks.js";var f={name:"Autocomplete",purpose:"Free-text field with suggestion popup: input has real focus, highlighted suggestion is virtual (aria-activedescendant). Commits on Enter/click/Tab/blur. Allows values outside the list by default and announces suggestion count as the list narrows. Derives List anatomy from options array or hand-composed children.",props:[{name:"value",type:"string",required:!1,description:"The controlled committed value, paired with the value-change event; a separate state from inputValue."},{name:"defaultValue",type:"string",defaultValue:"''",required:!1,description:"Initial committed value, for uncontrolled usage; also seeds defaultInputValue."},{name:"inputValue",type:"string",required:!1,description:"Controlled draft text, paired with input-value-change; with no `filter` predicate, this prop is the filtering mechanism."},{name:"defaultInputValue",type:"string",defaultValue:"defaultValue",required:!1,description:"Initial draft text, for uncontrolled usage, defaulting to the committed value."},{name:"open",type:"boolean",required:!1,description:"Controlled open state of the suggestion popup. Pair with the open-change event."},{name:"defaultOpen",type:"boolean",defaultValue:"false",required:!1,description:"Initial open state, for uncontrolled usage."},{name:"allowCustomValue",type:"boolean",defaultValue:"true",required:!1,description:"Lets the committed value be text outside the suggestion list. Off: an unmatched draft is discarded on blur."},{name:"openOnFocus",type:"boolean",defaultValue:"false",required:!1,description:"Opens the suggestion popup as soon as the input takes focus, instead of on the first keystroke."},{name:"filter",type:"'manual' | ((itemText: string, inputValue: string) => boolean | number)",required:!1,description:"Predicate filtering items against draft text; rejected items render nothing at all. `'manual'` filters nothing, exactly what omitting the prop does for async/server-side search. With `options` supplied and no `filter`, a case-insensitive text match narrows the derived list. Property-only on `<skdx-autocomplete>`. A number ranks and filters: 0 excludes, positive scores order survivors."},{name:"autoHighlight",type:"boolean",defaultValue:"false",required:!1,description:"Keeps the first suggestion highlighted whenever the filtered list changes while open, so Enter has an obvious target. Off by default, since the draft text is itself a valid answer here."},{name:"loop",type:"boolean",defaultValue:"true",required:!1,description:"Wraps arrow navigation around the ends of the suggestion list. On by default, matching Combobox; Home/End always jump to an end regardless."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Blocks all interaction: the input is natively disabled, so the popup can never open."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Shows the committed text but blocks changes; natively readOnly and still focusable, unlike disabled."},{name:"invalid",type:"boolean",defaultValue:"false",required:!1,description:"Marks the field invalid, exposed as aria-invalid and data-invalid on the Input."},{name:"loading",type:"boolean",defaultValue:"false",required:!1,description:'Marks suggestions in flight as `aria-busy="true"` and `data-loading`, and suppresses the suggestion-count announcement.'},{name:"name",type:"string",required:!1,description:"Form field name; what submits is the committed value, never a suggestion."},{name:"form",type:"string | undefined",required:!1,defaultValue:"undefined",description:"Id of the `<form>` the field submits into. Web Components names this `formId`."},{name:"getResultCountLabel",type:"(count: number) => string",defaultValue:"defaultGetSuggestionCountLabel",required:!1,description:"Formats the message announced when the rendered suggestion count changes while open. Return an empty string to announce nothing. Property-only."},{name:"options",type:"Array<{ value: string; label?: string; disabled?: boolean } | { label: string; options: Array<{ value: string; label?: string; disabled?: boolean }> }>",required:!1,description:"Declares the suggestion collection as data instead of hand-composed Item/Group children; an entry with its own `options` array is a group. Property-only on `<skdx-autocomplete>`."},{name:"renderOption",type:"(option: AutocompleteOptionData, state: { selected: boolean }) => ReactNode",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:'Web Components has no property-only equivalent \u2014 an HTML attribute cannot carry a function \u2014 and expresses the same customisation as a named light-DOM slot, the platform-native form of the hook, exactly as Vue expresses it as a scoped slot: `slot="option"`, keyed to its suggestion by `data-value`. Content is therefore authored per entry the consumer already knows about rather than computed per datum; an entry in the data with no matching slotted node falls back to its plain label, which is what the other ports render when their hook returns nothing. Vue expresses the same customisation as a scoped slot (`#option`) rather than a declared prop \u2014 its own idiomatic mechanism, invisible to a prop extractor but fully supported.',description:"Custom rendering for an `options`-derived Item, given the suggestion data and whether it matches the committed value. Ignored for hand-composed Item children."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text forwarded to the Input. Ignored under full manual composition."},{name:"aria-label",type:"string",required:!1,frameworks:["react","vue","svelte","angular"],exception:"attribute-passthrough",frameworksReason:"All four component packages that can declare a prop now do: React forwards it as the literal `aria-label` prop, Vue and Svelte declare it (Vue as `ariaLabel`, which the extractor folds back to `aria-label`), and Angular takes it as `ariaLabel`. All five settle on one resolution rule: the derived name is `aria-label ?? placeholder`, with no attribute emitted when neither is given and no hardcoded English fallback. Web Components is outside this list because it declares no property at all \u2014 it claims the native `aria-label` attribute off the host and relocates it onto the derived control, which is the platform mechanism for the same capability and is invisible to a prop extractor.",description:"Convenience accessible name for the Input. Equivalent to naming Input directly under manual composition."},{name:"clearable",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived anatomy renders a Clear button that empties the draft text and committed value in one action. Off by default."},{name:"emptyMessage",frameworks:["angular","svelte"],exception:"derived-anatomy",frameworksReason:"Angular and Svelte fold the Empty part into their derived anatomy behind this switch. React, Vue and Web Components publish the same part for the consumer to place by hand, so the prop would only restate what writing the part already says.",type:"string",required:!1,description:"Content for the derived Empty part, shown when no suggestions are rendered; absent, the package renders a hardcoded placeholder string."}],events:[{name:"value-change",description:`${a("the committed value")} Fires on Enter, suggestion click, Tab and blur, never on a keystroke. \`created\` is true when the commit was the draft text itself rather than a suggestion.`,detail:e("string","created: boolean")},{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, the change reason and the created flag.",detail:e("string","created: boolean")},{name:"input-value-change",description:"Fired with the new draft text on every keystroke, and whenever a commit or a revert rewrites the field.",detail:"string"},{name:"open-change",detail:"boolean",description:"Fired with the new open state whenever the popup opens or closes, controlled or not."},s("popup"),...i("popup")],parts:[{name:"Input",lightDom:!0,description:'The free-text field, which owns real DOM focus for the whole interaction and carries role="combobox", aria-expanded, aria-controls, aria-autocomplete, and the aria-activedescendant naming the highlighted suggestion.'},{name:"Trigger",lightDom:!0,description:"An optional button that toggles the suggestion popup, for the pattern where the field should also behave like a dropdown. Focus stays on the input either way."},{name:"Clear",description:"An optional button that empties both the draft text and the committed value in one action and returns focus to the Input. Natively disabled while there is nothing to clear or the root is disabled/readOnly; cancels its own mousedown so DOM focus never leaves the Input."},{...d,props:[...h,{...t,frameworks:["react","vue","svelte"],exception:"force-mount",frameworksReason:"Web Components never unmounts a closed overlay \u2014 the part hosts the consumer's own adopted light-DOM children and only hides them, so there is no unmount for forceMount to prevent and no exit animation it could rescue. Angular ships no Portal part on any overlay family at all \u2014 it portals with the [skdxPortal] attribute directive instead, so there is no Angular Portal for the prop to apply to."}]},{name:"Content",description:"The popup shell, positioned against the input through the shared popper layer. Defaults side to bottom, align to start, and sideOffset to 4, rather than the popper base defaults.",props:[...r({align:"'start'",sideOffset:"4"}),{...t,frameworks:["react","vue","svelte"],exception:"force-mount",frameworksReason:"Web Components never unmounts a closed overlay \u2014 the part hosts the consumer's own adopted light-DOM children and only hides them, so there is no unmount for forceMount to prevent and no exit animation it could rescue. Angular hosts this part as the component itself, kept in the tree and toggled with [hidden] rather than unmounted, so it ships no forceMount either."}]},{name:"List",description:`The role="listbox" the suggestions live in, referenced by the input's aria-controls.`},{name:"Item",description:'One suggestion (role="option"). Highlighted virtually, not focused, so the caret stays on the input.',props:[{name:"value",type:"string",required:!0,description:"Value committed to the root when this suggestion is chosen."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Excludes this suggestion from selection and pointer highlight."},{name:"textValue",type:"string",required:!1,description:"Text the root's filter predicate matches against, and the text a commit writes back into the Input; falls back to rendered text content."}]},{name:"Group",description:'A named section of related Items (role="group") whose aria-labelledby points at its own GroupLabel. Structural only \u2014 registers no suggestions and takes no part in the highlight order.'},{name:"GroupLabel",description:"The heading that names a Group. Carries the id its Group points aria-labelledby at and no role of its own, so the heading never becomes a suggestion the arrow keys can land on. Rendering one outside a Group is an error in every package."},{name:"Empty",description:"Shown in place of the suggestion list when no items are rendered."}],slots:[{name:"default",description:"Content's body \u2014 a List of Items or Groups, and an Empty state."}],states:["closed","open","highlighted","selected","disabled","readonly","invalid","loading"],controlledUncontrolled:"Three independent controlled/uncontrolled axes \u2014 value, inputValue, and open \u2014 each starting from its default* prop and switching to controlled when the matching prop is passed. The last committed text is always held internally, since Escape has to put it back whether or not the value is controlled. In Angular the pair is split: `valueChange` emits the scalar for `[(value)]`, `valueChangeDetail` carries the detail.",formBehavior:"With `name` set the field submits the text in the box. React, Vue, Svelte and Angular stamp it on the real `<input>` the Input renders; submission and validation are the platform's own. Web Components cannot: the input lives in a shadow root so it is form-associated through ElementInternals, publishing value and validity. The observable surface is the same across all five: root-level `name`, checkValidity(), reportValidity(). `form` associates with a form it is not nested inside; Web Components uses `formId`.",validationBehavior:"Native constraint validation on the underlying input, plus the presentational invalid flag. No list-membership rule: a value outside the list is the expected case here, not an error. checkValidity()/reportValidity() report the browser's own answer for that input and return true when no Input is mounted, since a control that submits nothing has nothing to be invalid about.",accessibility:{role:"combobox (Input), button (Trigger, Clear), listbox (List), option (Item), group (Group)",keyboard:[{key:"ArrowDown",behavior:"Opens the popup highlighting the first suggestion, or moves to the next one."},{key:"ArrowUp",behavior:"Opens the popup highlighting the last suggestion, or moves to the previous one."},{key:"Alt+ArrowDown",behavior:`Opens the popup and highlights nothing \u2014 APG's optional "show me the list" key. The missing highlight is the whole difference from the plain key, and it is what keeps Enter committing the draft text rather than a suggestion the user never pointed at. Prevents the default.`},{key:"Alt+ArrowUp",behavior:"Commits the highlighted suggestion and closes the popup; with nothing highlighted it only closes. Prevents the default."},{key:"Home / End (popup open)",behavior:"Moves highlight to the first / last enabled suggestion. Open only: closed, both are the field's caret keys and must not be intercepted."},{key:"Enter",behavior:"Commits the highlighted suggestion, or the raw draft text when nothing is highlighted and custom values are allowed."},{key:"Escape",behavior:"Closes the popup and puts the last committed text back in the field."},{key:"Tab",behavior:"Commits the draft and lets focus move on \u2014 deliberately not prevented, since trapping Tab in a text field is how a keyboard user gets stuck."}],ariaAttributes:['role="combobox" with aria-expanded, aria-controls, and aria-autocomplete on the Input',"aria-activedescendant on the Input naming the highlighted option \u2014 the highlight is virtual, so DOM focus never leaves the field","aria-selected on the option matching the committed value","aria-disabled and data-disabled on a disabled suggestion, which is skipped by navigation","aria-invalid and aria-readonly on the Input, mirroring the matching props",'aria-busy="true" and data-loading on Input and Content while `loading`',"The suggestion count is announced politely as the list narrows, because filtering changes nothing else that assistive technology reports \u2014 and is suppressed entirely while `loading`, since a total measured mid-request is not the answer",'role="group" with an aria-labelledby resolving to the GroupLabel inside it on Group, and tabindex="-1" on Trigger and Clear, which are affordances beside the field rather than tab stops of their own',"The highlighted suggestion is scrolled into view on every highlight move, so a keyboard user is never navigating a list they cannot see. Opening scrolls the popup itself rather than walking scrollable ancestors, which would take the page with it while the popup is still unpositioned"],focusManagement:'Virtual focus, per the collection-focus rule: a text input owns DOM focus in front of a popup list, so DOM focus stays on the Input for the entire interaction \u2014 including while arrowing through suggestions and while the optional Trigger is clicked \u2014 and the highlighted suggestion is named by aria-activedescendant on the Input and mirrored as data-highlighted on the item. That is what lets the user keep typing mid-navigation, and what makes Tab behave like Tab. Because virtual focus scrolls nothing on its own, every keyboard move of the highlight brings the suggestion into view with scrollIntoView({ block: "nearest" }), and a highlight the component assigns itself while the popup is opening scrolls the popup rather than an ancestor.',namingRequirement:'The Input must be named (via label, aria-label, or aria-labelledby); role="combobox" takes no name from content. The List should be named but is not required. Trigger and Clear must be named. Group needs only its GroupLabel to have text.'},styling:{cssParts:["field","input","trigger","clear","content","list","item","group","label","empty"],dataAttributes:[...u,"data-clearable","data-state","data-highlighted","data-disabled","data-readonly","data-invalid","data-loading","data-value","data-side","data-align",{name:"data-skdx-autocomplete-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:l("autocomplete")},publicMethods:[...n,o]};export{f as autocompleteContract};
|
|
1
|
+
import{changeDescription as a,changeDetail as e}from"./change-reason.js";import{constraintValidationMethods as n,setCustomValidityMethod as o}from"./form-validity.js";import{dismissalEvents as i,forceMountProp as t,openChangeCompleteEvent as r,popperContentProps as s,popperMeasurementProperties as l,portalPart as d,portalPropsSansForceMount as h}from"./overlay-parts.js";import{PART_DATA_ATTRIBUTES as p}from"./styling-hooks.js";import{withoutScope as u}from"./types.js";var v={name:"Autocomplete",purpose:"Free-text field with suggestion popup: input has real focus, highlighted suggestion is virtual (aria-activedescendant). Commits on Enter/click/Tab/blur. Allows values outside the list by default and announces suggestion count as the list narrows. Derives List anatomy from options array or hand-composed children.",props:[{name:"value",type:"string",required:!1,description:"The controlled committed value, paired with the value-change event; a separate state from inputValue."},{name:"defaultValue",type:"string",defaultValue:"''",required:!1,description:"Initial committed value, for uncontrolled usage; also seeds defaultInputValue."},{name:"inputValue",type:"string",required:!1,description:"Controlled draft text, paired with input-value-change; with no `filter` predicate, this prop is the filtering mechanism."},{name:"defaultInputValue",type:"string",defaultValue:"defaultValue",required:!1,description:"Initial draft text, for uncontrolled usage, defaulting to the committed value."},{name:"open",type:"boolean",required:!1,description:"Controlled open state of the suggestion popup. Pair with the open-change event."},{name:"defaultOpen",type:"boolean",defaultValue:"false",required:!1,description:"Initial open state, for uncontrolled usage."},{name:"allowCustomValue",type:"boolean",defaultValue:"true",required:!1,description:"Lets the committed value be text outside the suggestion list. Off: an unmatched draft is discarded on blur."},{name:"openOnFocus",type:"boolean",defaultValue:"false",required:!1,description:"Opens the suggestion popup as soon as the input takes focus, instead of on the first keystroke."},{name:"filter",type:"'manual' | ((itemText: string, inputValue: string) => boolean | number)",required:!1,description:"Predicate filtering items against draft text; rejected items render nothing at all. `'manual'` filters nothing, exactly what omitting the prop does for async/server-side search. With `options` supplied and no `filter`, a case-insensitive text match narrows the derived list. Property-only on `<skdx-autocomplete>`. A number ranks and filters: 0 excludes, positive scores order survivors."},{name:"autoHighlight",type:"boolean",defaultValue:"false",required:!1,description:"Keeps the first suggestion highlighted whenever the filtered list changes while open, so Enter has an obvious target. Off by default, since the draft text is itself a valid answer here."},{name:"loop",type:"boolean",defaultValue:"true",required:!1,description:"Wraps arrow navigation around the ends of the suggestion list. On by default, matching Combobox; Home/End always jump to an end regardless."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Blocks all interaction: the input is natively disabled, so the popup can never open."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:"Shows the committed text but blocks changes; natively readOnly and still focusable, unlike disabled."},{name:"invalid",type:"boolean",defaultValue:"false",required:!1,description:"Marks the field invalid, exposed as aria-invalid and data-invalid on the Input."},{name:"loading",type:"boolean",defaultValue:"false",required:!1,description:'Marks suggestions in flight as `aria-busy="true"` and `data-loading`, and suppresses the suggestion-count announcement.'},{name:"name",type:"string",required:!1,description:"Form field name; what submits is the committed value, never a suggestion."},{name:"form",type:"string | undefined",required:!1,defaultValue:"undefined",description:"Id of the `<form>` the field submits into. Web Components names this `formId`."},{name:"getResultCountLabel",type:"(count: number) => string",defaultValue:"defaultGetSuggestionCountLabel",required:!1,description:"Formats the message announced when the rendered suggestion count changes while open. Return an empty string to announce nothing. Property-only."},{name:"options",type:"Array<{ value: string; label?: string; disabled?: boolean } | { label: string; options: Array<{ value: string; label?: string; disabled?: boolean }> }>",required:!1,description:"Declares the suggestion collection as data instead of hand-composed Item/Group children; an entry with its own `options` array is a group. Property-only on `<skdx-autocomplete>`."},{name:"renderOption",type:"(option: AutocompleteOptionData, state: { selected: boolean }) => ReactNode",required:!1,frameworks:["react","svelte","angular"],exception:"render-prop",frameworksReason:'Web Components has no property-only equivalent \u2014 an HTML attribute cannot carry a function \u2014 and expresses the same customisation as a named light-DOM slot, the platform-native form of the hook, exactly as Vue expresses it as a scoped slot: `slot="option"`, keyed to its suggestion by `data-value`. Content is therefore authored per entry the consumer already knows about rather than computed per datum; an entry in the data with no matching slotted node falls back to its plain label, which is what the other ports render when their hook returns nothing. Vue expresses the same customisation as a scoped slot (`#option`) rather than a declared prop \u2014 its own idiomatic mechanism, invisible to a prop extractor but fully supported.',description:"Custom rendering for an `options`-derived Item, given the suggestion data and whether it matches the committed value. Ignored for hand-composed Item children."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text forwarded to the Input. Ignored under full manual composition."},{name:"aria-label",type:"string",required:!1,frameworks:["react","vue","svelte","angular"],exception:"attribute-passthrough",frameworksReason:"All four component packages that can declare a prop now do: React forwards it as the literal `aria-label` prop, Vue and Svelte declare it (Vue as `ariaLabel`, which the extractor folds back to `aria-label`), and Angular takes it as `ariaLabel`. All five settle on one resolution rule: the derived name is `aria-label ?? placeholder`, with no attribute emitted when neither is given and no hardcoded English fallback. Web Components is outside this list because it declares no property at all \u2014 it claims the native `aria-label` attribute off the host and relocates it onto the derived control, which is the platform mechanism for the same capability and is invisible to a prop extractor.",description:"Convenience accessible name for the Input. Equivalent to naming Input directly under manual composition."},{name:"clearable",type:"boolean",defaultValue:"false",required:!1,description:"Whether the derived anatomy renders a Clear button that empties the draft text and committed value in one action. Off by default."},{name:"emptyMessage",frameworks:["angular","svelte"],exception:"derived-anatomy",frameworksReason:"Angular and Svelte fold the Empty part into their derived anatomy behind this switch. React, Vue and Web Components publish the same part for the consumer to place by hand, so the prop would only restate what writing the part already says.",type:"string",required:!1,description:"Content for the derived Empty part, shown when no suggestions are rendered; absent, the package renders a hardcoded placeholder string."}],events:[{name:"value-change",description:`${a("the committed value")} Fires on Enter, suggestion click, Tab and blur, never on a keystroke. \`created\` is true when the commit was the draft text itself rather than a suggestion.`,detail:e("string","created: boolean")},{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, the change reason and the created flag.",detail:e("string","created: boolean")},{name:"input-value-change",description:"Fired with the new draft text on every keystroke, and whenever a commit or a revert rewrites the field.",detail:"string"},{name:"open-change",detail:"boolean",description:"Fired with the new open state whenever the popup opens or closes, controlled or not."},r("popup"),...i("popup")],parts:[{name:"Input",lightDom:!0,description:'The free-text field, which owns real DOM focus for the whole interaction and carries role="combobox", aria-expanded, aria-controls, aria-autocomplete, and the aria-activedescendant naming the highlighted suggestion.'},{name:"Trigger",lightDom:!0,description:"An optional button that toggles the suggestion popup, for the pattern where the field should also behave like a dropdown. Focus stays on the input either way."},{name:"Clear",description:"An optional button that empties both the draft text and the committed value in one action and returns focus to the Input. Natively disabled while there is nothing to clear or the root is disabled/readOnly; cancels its own mousedown so DOM focus never leaves the Input."},{...d,props:[...h,{...t,frameworks:["react","vue","svelte"],exception:"force-mount",frameworksReason:"Web Components never unmounts a closed overlay \u2014 the part hosts the consumer's own adopted light-DOM children and only hides them, so there is no unmount for forceMount to prevent and no exit animation it could rescue. Angular ships no Portal part on any overlay family at all \u2014 it portals with the [skdxPortal] attribute directive instead, so there is no Angular Portal for the prop to apply to."}]},{name:"Content",description:"The popup shell, positioned against the input through the shared popper layer. Defaults side to bottom, align to start, and sideOffset to 4, rather than the popper base defaults.",props:[...s({align:"'start'",sideOffset:"4"}),u(t)]},{name:"List",description:`The role="listbox" the suggestions live in, referenced by the input's aria-controls.`},{name:"Item",description:'One suggestion (role="option"). Highlighted virtually, not focused, so the caret stays on the input.',props:[{name:"value",type:"string",required:!0,description:"Value committed to the root when this suggestion is chosen."},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Excludes this suggestion from selection and pointer highlight."},{name:"textValue",type:"string",required:!1,description:"Text the root's filter predicate matches against, and the text a commit writes back into the Input; falls back to rendered text content."}]},{name:"Group",description:'A named section of related Items (role="group") whose aria-labelledby points at its own GroupLabel. Structural only \u2014 registers no suggestions and takes no part in the highlight order.'},{name:"GroupLabel",description:"The heading that names a Group. Carries the id its Group points aria-labelledby at and no role of its own, so the heading never becomes a suggestion the arrow keys can land on. Rendering one outside a Group is an error in every package."},{name:"Empty",description:"Shown in place of the suggestion list when no items are rendered."}],slots:[{name:"default",description:"Content's body \u2014 a List of Items or Groups, and an Empty state."},{name:"option",frameworks:["vue","webComponents"],exception:"render-prop",frameworksReason:"vue, webComponents expose option as native content projection for the renderOption rendering capability. Other frameworks pass the documented renderOption render prop or template instead of a named slot with this spelling.",description:"Custom rendering for an `options`-derived Item, given the suggestion data and whether it matches the committed value. Ignored for hand-composed Item children."},{name:"render-option",frameworks:["svelte"],exception:"render-prop",frameworksReason:"svelte expose render-option as native content projection for the renderOption rendering capability. Other frameworks pass the documented renderOption render prop or template instead of a named slot with this spelling.",description:"Custom rendering for an `options`-derived Item, given the suggestion data and whether it matches the committed value. Ignored for hand-composed Item children."}],states:["closed","open","highlighted","selected","disabled","readonly","invalid","loading"],controlledUncontrolled:"Three independent controlled/uncontrolled axes \u2014 value, inputValue, and open \u2014 each starting from its default* prop and switching to controlled when the matching prop is passed. The last committed text is always held internally, since Escape has to put it back whether or not the value is controlled. In Angular the pair is split: `valueChange` emits the scalar for `[(value)]`, `valueChangeDetail` carries the detail.",formBehavior:"With `name` set the field submits the text in the box. React, Vue, Svelte and Angular stamp it on the real `<input>` the Input renders; submission and validation are the platform's own. Web Components cannot: the input lives in a shadow root so it is form-associated through ElementInternals, publishing value and validity. The observable surface is the same across all five: root-level `name`, checkValidity(), reportValidity(). `form` associates with a form it is not nested inside; Web Components uses `formId`.",validationBehavior:"Native constraint validation on the underlying input, plus the presentational invalid flag. No list-membership rule: a value outside the list is the expected case here, not an error. checkValidity()/reportValidity() report the browser's own answer for that input and return true when no Input is mounted, since a control that submits nothing has nothing to be invalid about.",accessibility:{role:"combobox (Input), button (Trigger, Clear), listbox (List), option (Item), group (Group)",keyboard:[{key:"ArrowDown",behavior:"Opens the popup highlighting the first suggestion, or moves to the next one."},{key:"ArrowUp",behavior:"Opens the popup highlighting the last suggestion, or moves to the previous one."},{key:"Alt+ArrowDown",behavior:`Opens the popup and highlights nothing \u2014 APG's optional "show me the list" key. The missing highlight is the whole difference from the plain key, and it is what keeps Enter committing the draft text rather than a suggestion the user never pointed at. Prevents the default.`},{key:"Alt+ArrowUp",behavior:"Commits the highlighted suggestion and closes the popup; with nothing highlighted it only closes. Prevents the default."},{key:"Home / End (popup open)",behavior:"Moves highlight to the first / last enabled suggestion. Open only: closed, both are the field's caret keys and must not be intercepted."},{key:"Enter",behavior:"Commits the highlighted suggestion, or the raw draft text when nothing is highlighted and custom values are allowed."},{key:"Escape",behavior:"Closes the popup and puts the last committed text back in the field."},{key:"Tab",behavior:"Commits the draft and lets focus move on \u2014 deliberately not prevented, since trapping Tab in a text field is how a keyboard user gets stuck."}],ariaAttributes:['role="combobox" with aria-expanded, aria-controls, and aria-autocomplete on the Input',"aria-activedescendant on the Input naming the highlighted option \u2014 the highlight is virtual, so DOM focus never leaves the field","aria-selected on the option matching the committed value","aria-disabled and data-disabled on a disabled suggestion, which is skipped by navigation","aria-invalid and aria-readonly on the Input, mirroring the matching props",'aria-busy="true" and data-loading on Input and Content while `loading`',"The suggestion count is announced politely as the list narrows, because filtering changes nothing else that assistive technology reports \u2014 and is suppressed entirely while `loading`, since a total measured mid-request is not the answer",'role="group" with an aria-labelledby resolving to the GroupLabel inside it on Group, and tabindex="-1" on Trigger and Clear, which are affordances beside the field rather than tab stops of their own',"The highlighted suggestion is scrolled into view on every highlight move, so a keyboard user is never navigating a list they cannot see. Opening scrolls the popup itself rather than walking scrollable ancestors, which would take the page with it while the popup is still unpositioned"],focusManagement:'Virtual focus, per the collection-focus rule: a text input owns DOM focus in front of a popup list, so DOM focus stays on the Input for the entire interaction \u2014 including while arrowing through suggestions and while the optional Trigger is clicked \u2014 and the highlighted suggestion is named by aria-activedescendant on the Input and mirrored as data-highlighted on the item. That is what lets the user keep typing mid-navigation, and what makes Tab behave like Tab. Because virtual focus scrolls nothing on its own, every keyboard move of the highlight brings the suggestion into view with scrollIntoView({ block: "nearest" }), and a highlight the component assigns itself while the popup is opening scrolls the popup rather than an ancestor.',namingRequirement:'The Input must be named (via label, aria-label, or aria-labelledby); role="combobox" takes no name from content. The List should be named but is not required. Trigger and Clear must be named. Group needs only its GroupLabel to have text.'},styling:{cssParts:["field","input","trigger","clear","content","list","item","group","label","empty"],dataAttributes:[...p,"data-clearable","data-state","data-highlighted","data-disabled","data-readonly","data-invalid","data-loading","data-value","data-side","data-align",{name:"data-skdx-autocomplete-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:l("autocomplete")},publicMethods:[...n,o]};export{v as autocompleteContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var
|
|
1
|
+
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"Avatar",purpose:"A user/entity picture with a graceful fallback. The root loads Image.src itself (independent of the native <img> load event) and shares that status with Image and Fallback, so Fallback shows while loading/idle/errored and Image replaces it once loaded.",props:[{name:"label",type:"string",required:!1,description:"The avatar's accessible name (aria-label on root, default alt on Image). Without it, the name changes as the image loads \u2014 Fallback text while loading/idle/error, alt text once loaded. Avoid network-timing-dependent announcements by providing this prop."},{name:"decorative",type:"boolean",defaultValue:"false",required:!1,description:`On the root: marks the avatar decorative, setting aria-hidden="true" on the root and forcing alt="" on the Image part. On the root rather than on the Image, and that is the point \u2014 alt="" alone suppresses only the loaded state, while the Fallback's initials stay in the accessibility tree for every other one. An avatar rendered beside the name it depicts is otherwise announced twice.`},{name:"src",type:"string",required:!1,description:"Image source. When absent, or when the image fails to load, the fallback renders instead."},{name:"name",type:"string",required:!1,description:"The person or entity the avatar stands for. Supplies the fallback's initials, and the accessible name when `label` is absent."},{name:"fallbackDelayMs",type:"number",defaultValue:"0",required:!1,description:"Milliseconds to wait before showing the fallback, so it does not flash on a fast connection. The default renders synchronously rather than arming a timer."},{name:"shape",type:"'circle' | 'rounded' | 'square' | (string & {})",required:!1,description:"Corner rounding rung, written to data-shape on the root; the data-shape attribute may be set directly instead."},{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:"loading-status-change",detail:"'idle' | 'loading' | 'loaded' | 'error'",description:"On the Image part only: fires with the image's new loading status ('idle' | 'loading' | 'loaded' | 'error') on every transition \u2014 'loading' when the request starts, then 'loaded' or 'error', and straight to 'error' when both src and srcset are missing or empty. The root loads src off-DOM, so this is the only way a consumer can see that load happen (e.g. to show a skeleton)."}],parts:[{name:"Fallback",description:"Renders while the root's image-loading status is not 'loaded' \u2014 initials or a placeholder.",props:[{name:"delayMs",type:"number",required:!1,description:"Delay before showing the fallback, to avoid a flash on a fast-loading image. Omitted means render immediately."}],slots:[{name:"default",description:"Fallback's content \u2014 initials or a placeholder, shown until the image loads."}]}],slots:[{name:"default",description:"Avatar body \u2014 typically an Image part and a Fallback part."}],appearance:[{attribute:"data-shape",values:["circle","rounded","square"],stylesheet:"avatar.css"},{attribute:"data-size",values:["md","sm","lg"],stylesheet:"avatar.css"}],states:["idle","loading","loaded","error"],controlledUncontrolled:"Stateless \u2014 Avatar tracks image-loading status internally, not consumer-driven.",formBehavior:"Not applicable \u2014 Avatar participates in no form.",validationBehavior:"Not applicable \u2014 Avatar carries no value and participates in no validation.",accessibility:{role:"generic (native <span> root; Image is a native <img> and must receive alt text)",keyboard:[],ariaAttributes:["aria-label on the root, from the label prop \u2014 the one name that holds across all four load states",'aria-hidden="true" on the root while decorative is set, which covers the Fallback as well as the Image'],focusManagement:"Not focusable; not part of the tab order.",namingRequirement:`Set label on the root. It is the only name that survives the load: while the status is idle, loading or error there is no <img>, so alt names nothing and the accessible text is the Fallback's. The Image part still takes an alt \u2014 defaulted from label, forced to "" when the root is decorative, and never left absent, because an <img> with no alt at all announces its filename.`},styling:{cssParts:["avatar","image","fallback"],dataAttributes:[...e,"data-load-status","data-shape","data-size",{name:"data-skdx-avatar-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:"vue",part:"Image",name:"alt",type:"string",description:"Native <img> alt binding on vue AvatarImage. Alternative text; defaults to the root's label, forced to '' when the root is decorative. 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:"Image",name:"alt",type:"string",description:"Native <img> alt binding on angular AvatarImage. Alternative text; defaults to the root's `label`, and is forced to `''` when the root is `decorative`. 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:"Image",name:"alt",type:"string | undefined",description:"Native <img> alt binding on webComponents AvatarImage. Alternative text. Defaults to the parent avatar's `label`, and is forced to\n`''` when the parent is `decorative`. 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:"vue",part:"Image",name:"sizes",type:"string",description:"Native <img> sizes binding on vue AvatarImage. Sizes hint for `srcset`, forwarded to the rendered `<img>` and to the probe. 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:"Image",name:"sizes",type:"string",description:"Native <img> sizes binding on angular AvatarImage. Sizes hint for `srcset`, forwarded to the rendered `<img>` and to the probe. 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:"Image",name:"sizes",type:"string",description:"Native <img> sizes binding on webComponents AvatarImage. Sizes hint for `srcset`, forwarded to the rendered `<img>` and to the probe. 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:"vue",part:"Image",name:"srcset",type:"string",description:"Native <img> srcset binding on vue AvatarImage. Candidate set for a responsive image, forwarded to the rendered img and to the internal probe. 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:"Image",name:"srcset",type:"string",description:"Native <img> srcset binding on angular AvatarImage. Candidate set for a responsive image, forwarded to the rendered `<img>` and the internal probe, so the status describes the same resource that ends up displayed. 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:"Image",name:"srcset",type:"string",description:"Native <img> srcset binding on webComponents AvatarImage. Candidate set for a responsive image, forwarded to the rendered `<img>`\n*and* to the internal probe, so the status describes the same resource that\nends up displayed. 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:"imageLoadingStatus",type:"SkdxImageLoadingStatus",description:"Current loading status of the sibling `<skdx-avatar-image>`. 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:"imageLoadingStatus",type:"SkdxImageLoadingStatus",description:"Current loading status of the sibling `<skdx-avatar-image>`. 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 avatarContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var t={name:"Breadcrumb",purpose:"A structural breadcrumb trail. Renders a <nav> the consumer must label \u2014 layout and separators are composed entirely from children, no internal state.",props:[{name:"label",type:"string",required:!0,description:"Accessible name for the <nav> landmark, written to its aria-label. Required with no default, so applications in any language can name the landmark. Named `label` rather than `aria-label` because it is a declared prop of the component, not an ARIA attribute passed through \u2014 every package resolves it and stamps the attribute itself."},{name:"separator",type:"string",defaultValue:"'/'",required:!1,description:"Glyph Dividers fall back to when they have no children. A string, not a slot, so it reaches all packages through light-DOM. Per-crumb children override this."},{name:"items",type:"ReadonlyArray<{ label: string; href?: string; current?: boolean; ellipsis?: boolean }>",required:!1,description:'Trail data the breadcrumb renders from, instead of List/Item/Link/Page/Separator children. `label` is crumb text; `href` is the destination; `current` marks the user\'s page (aria-current="page"); `ellipsis` marks omitted middle items. When supplied, this becomes the source of truth.'}],events:[],parts:[{name:"List",description:"The trail's <ol>. An ordered list, because the trail's order is its meaning \u2014 it is the path, not a set."},{name:"Item",description:"One <li> in the trail, wrapping a Link, a Page or an Ellipsis."},{name:"Link",description:"A navigable ancestor; renders an <a>.",props:[{name:"href",type:"string",required:!1,description:"The link's destination. Native attribute passthrough in React and Vue, an explicit prop in Angular, Svelte and Web Components \u2014 declared here so a package that stops forwarding it is a contract failure rather than an unnoticed one."},{name:"current",type:"boolean",defaultValue:"false",required:!1,description:'Marks this link as the current page, emitting aria-current="page" and nothing when unset. The one aria-current case the APG makes mandatory for a breadcrumb, and the skin already styles `.skdx-breadcrumb__link[aria-current="page"]` \u2014 an attribute no package emitted. Use it when the current page is still a link; use BreadcrumbPage when it is not.'},{name:"target",frameworks:["vue","svelte","angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React renders the anchor through Primitive.a and spreads the caller's anchor attributes, so target reaches the element as the native attribute and asChild hands it to a router link instead; there is no declared prop for an extractor to see. The other four declare it because each one pairs it with the rel guard below.",type:"string",required:!1,description:"Native target attribute for the anchor, such as _blank to open a new browsing context."},{name:"rel",frameworks:["vue","svelte","angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React spreads anchor attributes onto Primitive.a, so rel passes through natively and the value stays entirely the caller's. The four that declare it also resolve it against target, which is why the prop exists there as a named input rather than a passthrough.",type:"string",required:!1,description:"Native rel attribute for the anchor; resolves to noopener noreferrer when target is _blank and no explicit value is given."}]},{name:"Page",description:'The current page as non-navigable text; renders a <span> with aria-current="page".'},{name:"Separator",description:"The divider between two items; renders an <li> hidden from assistive technology, since the list markup already separates the crumbs semantically."},{name:"Ellipsis",description:'A decorative stand-in for omitted middle items; renders a <span> with role="presentation" and aria-hidden="true".'}],slots:[{name:"default",description:"The trail's BreadcrumbList."}
|
|
1
|
+
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var t={name:"Breadcrumb",purpose:"A structural breadcrumb trail. Renders a <nav> the consumer must label \u2014 layout and separators are composed entirely from children, no internal state.",props:[{name:"label",type:"string",required:!0,description:"Accessible name for the <nav> landmark, written to its aria-label. Required with no default, so applications in any language can name the landmark. Named `label` rather than `aria-label` because it is a declared prop of the component, not an ARIA attribute passed through \u2014 every package resolves it and stamps the attribute itself."},{name:"separator",type:"string",defaultValue:"'/'",required:!1,description:"Glyph Dividers fall back to when they have no children. A string, not a slot, so it reaches all packages through light-DOM. Per-crumb children override this."},{name:"items",type:"ReadonlyArray<{ label: string; href?: string; current?: boolean; ellipsis?: boolean }>",required:!1,description:'Trail data the breadcrumb renders from, instead of List/Item/Link/Page/Separator children. `label` is crumb text; `href` is the destination; `current` marks the user\'s page (aria-current="page"); `ellipsis` marks omitted middle items. When supplied, this becomes the source of truth.'}],events:[],parts:[{name:"List",description:"The trail's <ol>. An ordered list, because the trail's order is its meaning \u2014 it is the path, not a set."},{name:"Item",description:"One <li> in the trail, wrapping a Link, a Page or an Ellipsis.",slots:[{name:"default",description:"A single BreadcrumbItem, typically a link or the current page."}]},{name:"Link",description:"A navigable ancestor; renders an <a>.",props:[{name:"href",type:"string",required:!1,description:"The link's destination. Native attribute passthrough in React and Vue, an explicit prop in Angular, Svelte and Web Components \u2014 declared here so a package that stops forwarding it is a contract failure rather than an unnoticed one."},{name:"current",type:"boolean",defaultValue:"false",required:!1,description:'Marks this link as the current page, emitting aria-current="page" and nothing when unset. The one aria-current case the APG makes mandatory for a breadcrumb, and the skin already styles `.skdx-breadcrumb__link[aria-current="page"]` \u2014 an attribute no package emitted. Use it when the current page is still a link; use BreadcrumbPage when it is not.'},{name:"target",frameworks:["vue","svelte","angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React renders the anchor through Primitive.a and spreads the caller's anchor attributes, so target reaches the element as the native attribute and asChild hands it to a router link instead; there is no declared prop for an extractor to see. The other four declare it because each one pairs it with the rel guard below.",type:"string",required:!1,description:"Native target attribute for the anchor, such as _blank to open a new browsing context."},{name:"rel",frameworks:["vue","svelte","angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React spreads anchor attributes onto Primitive.a, so rel passes through natively and the value stays entirely the caller's. The four that declare it also resolve it against target, which is why the prop exists there as a named input rather than a passthrough.",type:"string",required:!1,description:"Native rel attribute for the anchor; resolves to noopener noreferrer when target is _blank and no explicit value is given."}]},{name:"Page",description:'The current page as non-navigable text; renders a <span> with aria-current="page".'},{name:"Separator",description:"The divider between two items; renders an <li> hidden from assistive technology, since the list markup already separates the crumbs semantically.",slots:[{name:"default",description:"Separator between two items."}]},{name:"Ellipsis",description:'A decorative stand-in for omitted middle items; renders a <span> with role="presentation" and aria-hidden="true".'}],slots:[{name:"default",description:"The trail's BreadcrumbList."}],states:["default"],controlledUncontrolled:"Stateless \u2014 Breadcrumb holds no internal state.",formBehavior:"Not applicable \u2014 Breadcrumb does not participate in forms.",validationBehavior:"Not applicable \u2014 Breadcrumb carries no value and participates in no validation.",accessibility:{role:"nav (named by the required `label` prop, which the component writes to aria-label)",keyboard:[],ariaAttributes:["The root is a <nav> whose first structural descendant is an <ol>",'BreadcrumbLink sets aria-current="page" when its current prop is set','BreadcrumbPage sets aria-current="page"','BreadcrumbSeparator sets aria-hidden="true"','BreadcrumbEllipsis sets role="presentation" and aria-hidden="true"',"A real trigger for omitted items must be placed in the BreadcrumbItem *beside* the ellipsis, never inside it.","No part takes a count or a threshold."],focusManagement:"Not applicable \u2014 focus follows native link/button semantics of its children.",namingRequirement:"The <nav> landmark must be named through the required `label` prop, which every package stamps as aria-label. No default \u2014 each navigation landmark on a page must be distinct."},styling:{cssParts:["root","list","item","link","page","separator","ellipsis"],dataAttributes:[...e,{name:"data-skdx-breadcrumb-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{t as breadcrumbContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{webComponentsValidationMethods as e}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as t}from"./styling-hooks.js";var
|
|
1
|
+
import{webComponentsValidationMethods as e}from"./form-validity.js";import{ROOT_DATA_ATTRIBUTES as t}from"./styling-hooks.js";var i={name:"Button",purpose:"An accessible trigger for actions such as submitting a form or opening a dialog. Renders a native button element with behavior and accessibility only \u2014 visual style is entirely consumer CSS.",props:[{name:"as",type:"PolymorphicTag",defaultValue:"'button'",required:!1,frameworks:["react","vue","svelte","webComponents"],exception:"as-directive",frameworksReason:"Angular has no `as` input. Its element-choice mechanism is an attribute directive the consumer puts on their own element \u2014 `<section skdx-ng-card>` \u2014 which is the same capability spelled the way Angular's template compiler already reads, and eleven such directives predate this one.",description:"Element name to render instead of the default <button>, from the closed allowlist in POLYMORPHIC_TAGS. A tag name only, never a component: the list has to be closed for Lit, which needs a static-html literal per tag and cannot build one from a runtime string. React additionally reaches this through asChild, which takes a component and wins when both are set; Angular has no prop and uses an attribute directive on the consumer's own element instead."},{name:"loading",type:"boolean",defaultValue:"false",required:!1,description:"Marks the button busy (e.g. while submitting). Sets aria-busy, aria-disabled, data-loading and data-disabled, and suppresses activation in the click handler. It does not set the native disabled attribute: the button stays focusable and keeps its tab stop. data-disabled is set because a busy button will not act \u2014 that is what the hook means \u2014 and because a loading Button composed onto a non-button element through asChild has no :disabled pseudo-class for the skin to match; data-loading remains the narrower hook saying why."},{name:"loadingLabel",type:"string",defaultValue:"'Loading'",required:!1,description:"Describes what is in progress while `loading` is true. Rendered into a visually-hidden element inside the button and referenced by aria-describedby, so the busy state is a node in the accessibility tree rather than an aria-busy flag with nothing behind it \u2014 a screen reader otherwise announces the button's own label and nothing about the wait. One prop per string, per the localization convention. The matching visual affordance is the skin's, drawn from the existing data-loading attribute, so it costs no API."},{name:"focusableWhenDisabled",type:"boolean",defaultValue:"false",required:!1,description:`Keeps a disabled button in the tab order. With it set, disabled emits aria-disabled="true" and tabindex="0" in place of the native disabled attribute, and activation is suppressed in the component's own click handler, so the press still does not land. A disabled control removed from the tab order is unreachable by arrow-key navigation in a Toolbar or menu bar, where arrow keys own the focus. Web Components must also bypass ElementInternals form disabling for it, since that is a second, separate route into the same removal.`},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:"Native disabled state, set as the native disabled attribute. Outranks loading: when both are set the button is hard-disabled and emits no aria-busy, since a busy state on an unreachable element is announced to nobody."},{name:"type",type:"'button' | 'submit' | 'reset'",defaultValue:"'button'",required:!1,description:"Native button type. Defaults to 'button' so it never accidentally submits an enclosing form."},{name:"iconOnly",type:"boolean",required:!1,description:"Marks the button as carrying only a glyph, written to data-icon-only on the root; the data-icon-only attribute may be set directly instead."},{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:"status",type:"'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'danger' | (string & {})",required:!1,description:"Severity rung, written to data-status on the root; the data-status attribute may be set directly instead."},{name:"variant",type:"'secondary' | 'primary' | 'danger' | 'outline' | 'ghost' | (string & {})",required:!1,description:"Fill style rung, written to data-variant on the root; the data-variant attribute may be set directly instead."}],events:[{name:"click",detail:"void",description:"Fired on activation (pointer click, or Enter/Space while focused)."}],slots:[{name:"default",description:"Button content \u2014 text, an icon, or both."}],appearance:[{attribute:"data-icon-only",values:[],stylesheet:"button.css"},{attribute:"data-size",values:["md","sm","lg"],stylesheet:"button.css"},{attribute:"data-status",values:["neutral","brand","info","success","warning","danger"],stylesheet:"_status.css"},{attribute:"data-variant",values:["secondary","primary","danger","outline","ghost"],stylesheet:"button.css"}],states:["default","hover","focus","active","disabled","loading"],controlledUncontrolled:"Stateless \u2014 loading/disabled are plain inputs the consumer owns; Button holds no internal state.",formBehavior:"type='submit' participates in the nearest enclosing form like any native submit button; type='button' (the default) never does.",validationBehavior:"Not applicable \u2014 Button carries no value and participates in no validation.",accessibility:{role:"button (native <button> element)",keyboard:[{key:"Enter or Space",behavior:"Activates the button when it has focus (native browser behavior)."}],ariaAttributes:["aria-busy set to true while loading","aria-disabled set to true while loading \u2014 the button is still focusable and still receives the press, so this is the only thing telling the user the press will not land"],focusManagement:'Native tab order. A disabled button is removed from it by the native disabled attribute. A loading button is not: it is soft-disabled with aria-disabled, keeps its tab stop, and keeps the focus of the user who just activated it \u2014 the native attribute would make the browser blur that user to <body> at the same moment aria-busy started announcing, leaving the announcement on an element nobody is on. Activation is suppressed in the component\u2019s own click handler instead, which is also what stops a loading type="submit" from submitting.',namingRequirement:"Icon-only buttons must supply an accessible name (aria-label) since there is no visible text content."},styling:{cssParts:["button"],dataAttributes:[...t,"data-disabled","data-loading","data-icon-only","data-size","data-status","data-variant"],cssCustomProperties:[]},publicMethods:[...e],frameworkExtensions:[{framework:"webComponents",name:"requiredMessage",type:"string",description:"Custom validity message shown when the button is a required form control; Web Components only, because the message is set through ElementInternals and cannot be reached from markup."},{framework:"webComponents",name:"form",type:"HTMLFormElement | null",description:`The form this button submits or resets, associated the way a native control's form="" attribute does; Web Components only, since the other four render a real <button> the browser associates itself.`},{framework:"webComponents",name:"name",type:"string",description:"Submission name for the button, matching a native submit button's; Web Components only, for the same reason as form."},{framework:"angular",name:"form",type:"string",description:"Native <button> form binding on angular button. Id of the `<form>` this button submits or resets, for a button rendered\noutside it. Forwarded to the rendered `<button>` 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:"svelte",name:"href",type:"string | undefined",description:'Native <a> href binding on svelte button. Link URL when `as="a"`. Ignored on other elements. 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"}]};export{i as buttonContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{changeDetail as r}from"./change-reason.js";import{DATE_CHANGE_VALUE_TYPE as o,DATE_INPUT_RULE as e,DATE_MALFORMED_RULE as a,DATE_OUTPUT_RULE as n,DATE_PARSE_RULE as i}from"./date-value.js";import{ROOT_DATA_ATTRIBUTES as s}from"./styling-hooks.js";var t="Activating a cell in the month or year grid navigates and selects nothing: value is untouched and value-change never fires from a non-day grid, because value is a Date \u2014 a day \u2014 and a month or a year is not one. What it does instead is project focusedDate onto the chosen unit and step the view down one level (year -> month -> day), stopping at minView. The projection preserves the day-of-month under exactly the clamp paging already uses: the focused day-of-month is carried onto the chosen month and clamped to that month's last day, so picking February while the 31st is focused lands on the 28th (or the 29th) rather than spilling into March, and picking a non-leap year while February 29th is focused lands on February 28th. The displayed month follows the projection whenever it leaves every displayed month, firing month-change, so the day grid always opens on the month that was just picked; the moved cursor is reported through focus-change, and the step down through view-change.",c={name:"Calendar",purpose:"Month-grid date picker following WAI-ARIA grid, built on native Date math and Intl. No date-library dependency or bundled name tables. Selects single date, multiple dates, or start/end range over consecutive months. Derives complete anatomy (header, day grids, month/year views) or accepts child parts.",props:[{name:"selectionMode",type:"'single' | 'multiple' | 'range'",defaultValue:"'single'",required:!1,description:"Selection count and value shape: 'single' \u2192 Date | null, 'multiple' \u2192 Date[], 'range' \u2192 { start: Date; end: Date | null } | null. Uses union type because frameworks cannot discriminate on sibling props."},{name:"value",type:"Date | string | Array<Date | string> | { start: Date | string; end: Date | string | null } | null",required:!1,description:`Current selection, for controlled usage. Its shape follows selectionMode. ${e} The wire string is self-describing across all three modes: "2026-08-10" is a single date, "2026-08-10,2026-08-14" a set, "2026-08-10/2026-08-14" a range, and a trailing slash a range still anchored on its start. ${i} ${a}`},{name:"defaultValue",type:"Date | string | Array<Date | string> | { start: Date | string; end: Date | string | null } | null",defaultValue:"null for `single`/`range`, `[]` for `multiple`.",required:!1,description:`Initial selection, for uncontrolled usage. Its shape follows selectionMode, and it takes the same Date-or-ISO-string forms value does. ${e}`},{name:"month",type:"Date | string",required:!1,description:`Displayed month, for controlled usage. ${e} A bare "2026-08" is accepted alongside "2026-08-10"; either way only the year and month are read.`},{name:"defaultMonth",type:"Date | string",required:!1,description:`Initially displayed month, for uncontrolled usage. Defaults to value's month, or the current month. Takes the same Date-or-ISO-string forms month does, including the bare "2026-08".`},{name:"minDate",type:"Date | string",required:!1,description:`Earliest selectable date; earlier dates render disabled. ${e} ${a}`},{name:"maxDate",type:"Date | string",required:!1,description:`Latest selectable date; later dates render disabled. ${e} ${a}`},{name:"weekStartsOn",type:"0 | 1 | 2 | 3 | 4 | 5 | 6",defaultValue:"derived from locale via Intl.Locale week info, falling back to 0 (Sunday)",required:!1,description:"Day of week the grid's first column represents. Derived from locale via Intl.Locale week info when omitted."},{name:"pageBehavior",type:"'visible' | 'single'",defaultValue:"'visible'",required:!1,description:"'visible' pages by numberOfMonths months; 'single' moves one month. Only observable with numberOfMonths above 1."},{name:"today",type:"Date | string",defaultValue:"new Date(), read once when the calendar mounts",required:!1,description:'The date the calendar treats as today: the cell carrying data-today and aria-current="date". Read once at mount, not per render, to prevent cells straddling midnight. Necessary in server-rendered apps to prevent hydration mismatch.'},{name:"locale",type:"string",defaultValue:"the runtime's own locale",required:!1,description:"BCP 47 tag to derive month and weekday names via Intl.DateTimeFormat and default weekStartsOn. No implementation ships hardcoded month or day name. Grid is Gregorian because value is a native Date; non-Gregorian calendars render localized Gregorian names over Gregorian month grid."},{name:"numberOfMonths",type:"number",defaultValue:"1",required:!1,description:"Consecutive months to display. Render one Grid and Heading per month, each with matching monthOffset."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:'Non-editable: every cell stays focusable with tab stop, grid carries aria-readonly="true", and no interaction changes selection. Distinct from isDateDisabled (per-date) and disabled (removes tab order).'},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:'Disables the whole calendar: cells are not focusable, grid leaves tab order, carries aria-disabled="true". Use isDateDisabled for per-date disabling.'},{name:"isDateDisabled",type:"(date: Date) => boolean",required:!1,description:"Mark dates as disabled beyond minDate/maxDate. Disabled cell is not focusable, not clickable. Takes a Date, same as isDateUnavailable."},{name:"isDateUnavailable",type:"(date: Date) => boolean",required:!1,description:'Mark dates as unavailable. Unlike isDateDisabled, an unavailable cell stays keyboard-reachable and is announced via aria-disabled="true", allowing screen-reader users to land on it.'},{name:"focusedDate",type:"Date | string",required:!1,description:`The date holding the grid's roving tab stop, for controlled usage \u2014 the one cell with tabIndex 0. Distinct from value: focus moves with every arrow key and selects nothing, which is exactly why a calendar wanting to restore a cursor, or to mirror one between two calendars, cannot do it through value. ${e} ${a}`},{name:"defaultFocusedDate",type:"Date | string",defaultValue:"value's anchor date, else the displayed month's first day",required:!1,description:`Initially focused date for uncontrolled usage. ${e}`},{name:"outsideDaySelectable",type:"boolean",defaultValue:"false",required:!1,description:"Whether a day from an adjacent month can be picked. False (default) disables those cells; true enables them, keeps data-outside-month hook, and pages calendar to their month on selection."},{name:"weekNumbers",type:"boolean",defaultValue:"false",required:!1,description:'Whether each week row is labelled with its ISO-8601 week number. Independent of weekStartsOn: week numbering and grid start column answer different questions. Number renders as role="rowheader" cell with role="columnheader" above it, so body rows sit under matching header.'},{name:"view",type:"'day' | 'month' | 'year'",required:!1,description:`Which grid is showing, for controlled usage: the day grid, the twelve-month grid, or the twenty-year grid. ${t}`},{name:"defaultView",type:"'day' | 'month' | 'year'",defaultValue:"'day'",required:!1,description:"Which grid shows first, for uncontrolled usage. Clamped to minView/maxView."},{name:"minView",type:"'day' | 'month' | 'year'",defaultValue:"'day'",required:!1,description:"Most granular view the calendar steps down to. Raising it restricts navigation: with 'month' the day grid is unreachable."},{name:"maxView",type:"'day' | 'month' | 'year'",defaultValue:"'year'",required:!1,description:"Least granular view the calendar steps up to and how far the Heading trigger goes. Never below minView."},{name:"getSelectionAnnouncement",type:"(value: CalendarValue, locale: string | undefined) => string",required:!1,description:'Announce selection changes in multiple and range mode. Return empty string to announce nothing. Defaults to English wording e.g. "Range selected, 1 May to 8 May."'},{name:"getMonthAnnouncement",type:"(month: Date, locale: string | undefined) => string",defaultValue:"formatMonthLabel",required:!1,description:'Announce navigation button paging. Return empty string to announce nothing. Defaults to locale-formatted month label, e.g. "f\xE9vrier 2026"'}],events:[{name:"value-change",detail:r(o),description:`Fires with new selection when a cell is chosen. Never fires while readOnly or disabled. ${n}`},{name:"month-change",detail:"Date",description:"Fires with newly displayed month as a Date when navigation changes it."},{name:"focus-change",detail:"Date",description:`Fires with newly focused date when grid's roving tab stop moves (arrow key, Home/End, PageUp/PageDown, click, paging). Independent of value-change. ${n}`},{name:"view-change",detail:"'day' | 'month' | 'year'",description:"Fires with view now showing ('day', 'month', 'year') when Heading steps up or non-day cell steps down. Independent of month-change and focus-change."}],parts:[{name:"Grid",description:`role="grid" container for one month of cells, owning arrow-key navigation. One per displayed month. Renders only while view is 'day'.`,props:[{name:"monthOffset",type:"number",defaultValue:"0",required:!1,description:"Which of the numberOfMonths displayed months this grid renders, 0 being the first. Decides which dates its cells treat as outside-month."},{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Overrides the grid's accessible name, the locale-formatted month by default."}]},{name:"GridHead",description:'role="rowgroup" wrapper for the weekday header. Renders weekday row with short name and full name as aria-label.'},{name:"GridBody",description:'role="rowgroup" wrapper around the week rows.'},{name:"WeekNumber",description:'role="rowheader" cell with ISO-8601 week number, rendered at week row start when weekNumbers is set. rowheader rather than gridcell because the number labels its row.',props:[{name:"date",type:"Date | string",required:!0,description:`Any day of the week to number \u2014 the row's first cell is what GridBody passes. ${e}`}]},{name:"WeekNumberHeader",description:'role="columnheader" above the WeekNumber column, rendered when weekNumbers is set. Visually blank, named via aria-label.'},{name:"MonthGrid",lightDom:!0,description:`role="grid" container: twelve gridcells (three rows of four), one per month of focused year. Shows while view is 'month'. Full grid with accessible name, roving tabindex, arrow/Home/End/PageUp/PageDown at month unit. Unavailable months are aria-disabled="true", keeping them in roving order. ${t}`,props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Overrides the grid's accessible name, the locale-formatted month page by default."}]},{name:"YearGrid",lightDom:!0,description:`role="grid" container: twenty gridcells (five rows of four), one twenty-year page. Shows while view is 'year'. Same full grid model and aria-disabled rule as MonthGrid, one unit coarser. ${t}`,props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Overrides the grid's accessible name, the locale-formatted year page by default."}]},{name:"PreviousButton",description:'Button paging the calendar back one unit of the current view, named "Previous month", "Previous year" or "Previous 20 years" by default.',props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Accessible name for the button, replacing the view-dependent default."}]},{name:"NextButton",description:'Button paging the calendar forward one unit of the current view, named "Next month", "Next year" or "Next 20 years" by default.',props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Accessible name for the button, replacing the view-dependent default."}]},{name:"Heading",description:"Label for what is on screen and view-up trigger. Renders as button stepping view up when below maxView, otherwise plain label.",props:[{name:"monthOffset",type:"number",defaultValue:"0",required:!1,description:"Which of the displayed months to label, 0 being the first."}]},{name:"Cell",description:'role="gridcell" date button carrying state hooks (selected, today, outside-month, disabled, unavailable, data-range-*).',props:[{name:"date",type:"Date | string",required:!0,description:`The day this cell renders. Required and undefaulted: a cell has no meaning without one, and there is no sensible fallback \u2014 the grid supplies it while iterating a week, so it is the one prop every consumer writes. ${e}`}]}],slots:[{name:"default",description:"Replaces the derived anatomy entirely. Left empty, the calendar draws all of it. Takes the Calendar parts."},{name:"heading",description:"Displayed month's label; empty for locale-derived default."},{name:"cell",description:"A single selectable date cell."}],states:["default","selected","today","outside-month","disabled","unavailable","range-start","range-middle","range-end"],controlledUncontrolled:"value, month, focusedDate and view each work controlled or uncontrolled, independently of one another.",formBehavior:"Not applicable \u2014 Calendar does not itself submit; pair with DatePicker for a form-field trigger.",validationBehavior:"Not applicable \u2014 Calendar performs no validation beyond min/max/disabled/unavailable cell rendering. Range and multiple selections are announced through the shared LiveAnnouncer (the anchored start, then the completed range; the running count in multiple mode), because that state lives across several cells and no single aria-selected flip conveys it.",accessibility:{role:'grid (the date grid), rowgroup (GridHead and GridBody), row (each week and the weekday-name row), columnheader (each weekday name, and the week-number column when weekNumbers is on), rowheader (each week number), gridcell (each date). GridHead is a rowgroup, NOT a row, and GridBody is a rowgroup, not a bare wrapper \u2014 a role="row" nested in a role="row", or a plain div between the grid and its rows, is what axe caught as aria-required-children / aria-required-parent in all five frameworks; the implementations were fixed to match.',keyboard:[{key:"ArrowLeft/ArrowRight",behavior:"Move focus by one day."},{key:"ArrowUp/ArrowDown",behavior:"Move focus by one week."},{key:"Home/End",behavior:"Jump to the start/end of the focused week."},{key:"PageUp/PageDown",behavior:"Move focus by one month. Unlike the navigation buttons, which page by pageBehavior, the keyboard always moves a single month: it moves the focused *date*, and paging it by numberOfMonths would jump the cursor rather than the view."},{key:"Shift+PageUp/PageDown",behavior:"Move focus by one year."},{key:"ArrowLeft/ArrowRight (month/year grid)",behavior:"Move focus by one month, or by one year."},{key:"ArrowUp/ArrowDown (month/year grid)",behavior:"Move focus by one row \u2014 four months, or four years, since both grids are four columns wide."},{key:"Home/End (month/year grid)",behavior:"Jump to the first/last cell of the page: January/December, or the twenty-year page's first/last year."},{key:"PageUp/PageDown (month/year grid)",behavior:"Move focus by one whole page \u2014 a year in the month grid, twenty years in the year grid \u2014 and announce the new page, since paging replaces every cell and moves no focus assistive technology would report."},{key:"Shift+PageUp/PageDown (month/year grid)",behavior:"Move focus by ten pages \u2014 ten years, or two hundred."},{key:"Enter/Space (month/year grid)",behavior:"Navigate to the focused month or year and step the view down one level. Selects nothing: only the day grid selects."},{key:"Enter/Space",behavior:"Select the focused date (native button activation). In range mode the first press anchors the range start and the second closes it, ordering start/end so a backwards selection still yields start <= end; a third press starts over."}],ariaAttributes:["aria-selected on every gridcell in the selection \u2014 in range mode that is every date from start to end inclusive, not just the two endpoints",'aria-disabled="true" on an unavailable (but still focusable) gridcell',`aria-current="date" on the gridcell representing today, and on no other cell \u2014 the sighted-only data-today hook is not a substitute, because inside a grid of bare day numbers today is the one part of a cell's meaning that cannot be read off its own text`,"aria-multiselectable on the grid, false only in single-selection mode",'an accessible name on the grid naming its month, from the same Intl formatter the Heading uses \u2014 role="grid" is name-from-author only, so an unnamed one announces as a bare "grid", and with numberOfMonths above 1 there are several identical-sounding grids on screen. Emitted as aria-label and suppressed the moment the consumer supplies an aria-label or aria-labelledby of their own',`aria-label on every gridcell carrying the full localized date ("Monday, January 5, 2026"), from the same Intl formatter the selection announcement uses. A cell's own text is a bare day number, so an unlabelled cell announces as "14" with no month \u2014 which makes arrowing into an adjacent month, and a PageDown that changes it, completely silent. Localized by locale rather than by a prop, because it is derived from the value rather than invented by the widget`,'aria-readonly="true" on the grid while readOnly, and absent otherwise \u2014 a read-only calendar keeps every cell focusable and every tab stop, so this attribute is the only thing that distinguishes it from an editable one','aria-disabled="true" on the grid while the whole calendar is disabled, and absent otherwise \u2014 every cell is natively disabled and therefore silent, so the grid is the only element left to carry the state','an accessible name on MonthGrid and YearGrid too, for the same name-from-author reason the day grid needs one: the focused year ("2026") and the year page ("2020 - 2039"), from the same values the Heading renders, so the two never disagree about what is on screen',`aria-label on every month and year gridcell carrying the full localized unit name ("January 2026", "2026") \u2014 a month cell's own text is an abbreviated month name and a year cell's is four digits, neither of which says which year or which page it belongs to`,'aria-disabled="true" on a month or year gridcell no date can satisfy, which keeps it focusable rather than removing it from the grid \u2014 the unavailable model, not the disabled one, because a twelve-cell grid with silent gaps reads as a broken grid'],focusManagement:"A controlled value that moves to a date outside every displayed month takes the focused date and the displayed month with it, firing month-change \u2014 otherwise the grid keeps its only tab stop on a cell that is no longer rendered, and Tab reaches the calendar to land on nothing. Roving tabindex: only the currently focused date is a tab stop (tabIndex 0), all others -1, and that date is readable and settable as focusedDate with every move reported through focus-change. Unavailable dates keep their place in the roving order; disabled ones do not, and an outside-month day joins it only while outsideDaySelectable is on. A disabled calendar has no tab stop at all, because every cell carries the native disabled attribute. The month and year grids run the same roving model one unit coarser: the tab stop sits on the cell focusedDate falls in \u2014 its month, or its year \u2014 so the cursor survives every step up and down the view stack rather than resetting, and a cell no date can satisfy keeps its place in that order because it is aria-disabled rather than natively disabled.",namingRequirement:'Weekday columnheaders render the locale short name with the full locale name as aria-label, so screen readers say "Monday" rather than "Mon".'},styling:{cssParts:["calendar","header","heading","previous-button","next-button","grid","row","week-day","cell","grid-head","grid-body","month-grid","year-grid","view-cell","week-number","week-number-header"],dataAttributes:[...s,"data-state","data-today","data-outside-month","data-disabled","data-unavailable","data-range-start","data-range-middle","data-range-end","data-skdx-calendar-cell","data-skdx-calendar-view-cell",{name:"data-skdx-calendar-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:[{name:"setValue",signature:"(date: Date) => 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:"Moves the selection to date and emits value-change, exactly as picking that cell would."}]};export{t as CALENDAR_PROJECTION_RULE,c as calendarContract};
|
|
1
|
+
import{changeDetail as r}from"./change-reason.js";import{DATE_CHANGE_VALUE_TYPE as o,DATE_INPUT_RULE as e,DATE_MALFORMED_RULE as a,DATE_OUTPUT_RULE as n,DATE_PARSE_RULE as i}from"./date-value.js";import{ROOT_DATA_ATTRIBUTES as s}from"./styling-hooks.js";var t="Activating a cell in the month or year grid navigates and selects nothing: value is untouched and value-change never fires from a non-day grid, because value is a Date \u2014 a day \u2014 and a month or a year is not one. What it does instead is project focusedDate onto the chosen unit and step the view down one level (year -> month -> day), stopping at minView. The projection preserves the day-of-month under exactly the clamp paging already uses: the focused day-of-month is carried onto the chosen month and clamped to that month's last day, so picking February while the 31st is focused lands on the 28th (or the 29th) rather than spilling into March, and picking a non-leap year while February 29th is focused lands on February 28th. The displayed month follows the projection whenever it leaves every displayed month, firing month-change, so the day grid always opens on the month that was just picked; the moved cursor is reported through focus-change, and the step down through view-change.",c={name:"Calendar",purpose:"Month-grid date picker following WAI-ARIA grid, built on native Date math and Intl. No date-library dependency or bundled name tables. Selects single date, multiple dates, or start/end range over consecutive months. Derives complete anatomy (header, day grids, month/year views) or accepts child parts.",props:[{name:"selectionMode",type:"'single' | 'multiple' | 'range'",defaultValue:"'single'",required:!1,description:"Selection count and value shape: 'single' \u2192 Date | null, 'multiple' \u2192 Date[], 'range' \u2192 { start: Date; end: Date | null } | null. Uses union type because frameworks cannot discriminate on sibling props."},{name:"value",type:"Date | string | Array<Date | string> | { start: Date | string; end: Date | string | null } | null",required:!1,description:`Current selection, for controlled usage. Its shape follows selectionMode. ${e} The wire string is self-describing across all three modes: "2026-08-10" is a single date, "2026-08-10,2026-08-14" a set, "2026-08-10/2026-08-14" a range, and a trailing slash a range still anchored on its start. ${i} ${a}`},{name:"defaultValue",type:"Date | string | Array<Date | string> | { start: Date | string; end: Date | string | null } | null",defaultValue:"null for `single`/`range`, `[]` for `multiple`.",required:!1,description:`Initial selection, for uncontrolled usage. Its shape follows selectionMode, and it takes the same Date-or-ISO-string forms value does. ${e}`},{name:"month",type:"Date | string",required:!1,description:`Displayed month, for controlled usage. ${e} A bare "2026-08" is accepted alongside "2026-08-10"; either way only the year and month are read.`},{name:"defaultMonth",type:"Date | string",required:!1,description:`Initially displayed month, for uncontrolled usage. Defaults to value's month, or the current month. Takes the same Date-or-ISO-string forms month does, including the bare "2026-08".`},{name:"minDate",type:"Date | string",required:!1,description:`Earliest selectable date; earlier dates render disabled. ${e} ${a}`},{name:"maxDate",type:"Date | string",required:!1,description:`Latest selectable date; later dates render disabled. ${e} ${a}`},{name:"weekStartsOn",type:"0 | 1 | 2 | 3 | 4 | 5 | 6",defaultValue:"derived from locale via Intl.Locale week info, falling back to 0 (Sunday)",required:!1,description:"Day of week the grid's first column represents. Derived from locale via Intl.Locale week info when omitted."},{name:"pageBehavior",type:"'visible' | 'single'",defaultValue:"'visible'",required:!1,description:"'visible' pages by numberOfMonths months; 'single' moves one month. Only observable with numberOfMonths above 1."},{name:"today",type:"Date | string",defaultValue:"new Date(), read once when the calendar mounts",required:!1,description:'The date the calendar treats as today: the cell carrying data-today and aria-current="date". Read once at mount, not per render, to prevent cells straddling midnight. Necessary in server-rendered apps to prevent hydration mismatch.'},{name:"locale",type:"string",defaultValue:"the runtime's own locale",required:!1,description:"BCP 47 tag to derive month and weekday names via Intl.DateTimeFormat and default weekStartsOn. No implementation ships hardcoded month or day name. Grid is Gregorian because value is a native Date; non-Gregorian calendars render localized Gregorian names over Gregorian month grid."},{name:"numberOfMonths",type:"number",defaultValue:"1",required:!1,description:"Consecutive months to display. Render one Grid and Heading per month, each with matching monthOffset."},{name:"readOnly",type:"boolean",defaultValue:"false",required:!1,description:'Non-editable: every cell stays focusable with tab stop, grid carries aria-readonly="true", and no interaction changes selection. Distinct from isDateDisabled (per-date) and disabled (removes tab order).'},{name:"disabled",type:"boolean",defaultValue:"false",required:!1,description:'Disables the whole calendar: cells are not focusable, grid leaves tab order, carries aria-disabled="true". Use isDateDisabled for per-date disabling.'},{name:"isDateDisabled",type:"(date: Date) => boolean",required:!1,description:"Mark dates as disabled beyond minDate/maxDate. Disabled cell is not focusable, not clickable. Takes a Date, same as isDateUnavailable."},{name:"isDateUnavailable",type:"(date: Date) => boolean",required:!1,description:'Mark dates as unavailable. Unlike isDateDisabled, an unavailable cell stays keyboard-reachable and is announced via aria-disabled="true", allowing screen-reader users to land on it.'},{name:"focusedDate",type:"Date | string",required:!1,description:`The date holding the grid's roving tab stop, for controlled usage \u2014 the one cell with tabIndex 0. Distinct from value: focus moves with every arrow key and selects nothing, which is exactly why a calendar wanting to restore a cursor, or to mirror one between two calendars, cannot do it through value. ${e} ${a}`},{name:"defaultFocusedDate",type:"Date | string",defaultValue:"value's anchor date, else today when it is displayed, else the month's first day",required:!1,description:`Initially focused date for uncontrolled usage. ${e}`},{name:"outsideDaySelectable",type:"boolean",defaultValue:"false",required:!1,description:"Whether a day from an adjacent month can be picked. False (default) disables those cells; true enables them, keeps data-outside-month hook, and pages calendar to their month on selection."},{name:"weekNumbers",type:"boolean",defaultValue:"false",required:!1,description:'Whether each week row is labelled with its ISO-8601 week number. Independent of weekStartsOn: week numbering and grid start column answer different questions. Number renders as role="rowheader" cell with role="columnheader" above it, so body rows sit under matching header.'},{name:"view",type:"'day' | 'month' | 'year'",required:!1,description:`Which grid is showing, for controlled usage: the day grid, the twelve-month grid, or the twenty-year grid. ${t}`},{name:"defaultView",type:"'day' | 'month' | 'year'",defaultValue:"'day'",required:!1,description:"Which grid shows first, for uncontrolled usage. Clamped to minView/maxView."},{name:"minView",type:"'day' | 'month' | 'year'",defaultValue:"'day'",required:!1,description:"Most granular view the calendar steps down to. Raising it restricts navigation: with 'month' the day grid is unreachable."},{name:"maxView",type:"'day' | 'month' | 'year'",defaultValue:"'year'",required:!1,description:"Least granular view the calendar steps up to and how far the Heading trigger goes. Never below minView."},{name:"getSelectionAnnouncement",type:"(value: CalendarValue, locale: string | undefined) => string",required:!1,description:'Announce selection changes in multiple and range mode. Return empty string to announce nothing. Defaults to English wording e.g. "Range selected, 1 May to 8 May."'},{name:"getMonthAnnouncement",type:"(month: Date, locale: string | undefined) => string",defaultValue:"formatMonthLabel",required:!1,description:'Announce navigation button paging. Return empty string to announce nothing. Defaults to locale-formatted month label, e.g. "f\xE9vrier 2026"'}],events:[{name:"value-change",detail:r(o),description:`Fires with new selection when a cell is chosen. Never fires while readOnly or disabled. ${n}`},{name:"month-change",detail:"Date",description:"Fires with newly displayed month as a Date when navigation changes it."},{name:"focus-change",detail:"Date",description:`Fires with newly focused date when grid's roving tab stop moves (arrow key, Home/End, PageUp/PageDown, click, paging). Independent of value-change. ${n}`},{name:"view-change",detail:"'day' | 'month' | 'year'",description:"Fires with view now showing ('day', 'month', 'year') when Heading steps up or non-day cell steps down. Independent of month-change and focus-change."}],parts:[{name:"Grid",description:`role="grid" container for one month of cells, owning arrow-key navigation. One per displayed month. Renders only while view is 'day'.`,props:[{name:"monthOffset",type:"number",defaultValue:"0",required:!1,description:"Which of the numberOfMonths displayed months this grid renders, 0 being the first. Decides which dates its cells treat as outside-month."},{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Overrides the grid's accessible name, the locale-formatted month by default."}]},{name:"GridHead",description:'role="rowgroup" wrapper for the weekday header. Renders weekday row with short name and full name as aria-label.'},{name:"GridBody",description:'role="rowgroup" wrapper around the week rows.'},{name:"WeekNumber",description:'role="rowheader" cell with ISO-8601 week number, rendered at week row start when weekNumbers is set. rowheader rather than gridcell because the number labels its row.',props:[{name:"date",type:"Date | string",required:!0,description:`Any day of the week to number \u2014 the row's first cell is what GridBody passes. ${e}`}]},{name:"WeekNumberHeader",description:'role="columnheader" above the WeekNumber column, rendered when weekNumbers is set. Visually blank, named via aria-label.'},{name:"MonthGrid",lightDom:!0,description:`role="grid" container: twelve gridcells (three rows of four), one per month of focused year. Shows while view is 'month'. Full grid with accessible name, roving tabindex, arrow/Home/End/PageUp/PageDown at month unit. Unavailable months are aria-disabled="true", keeping them in roving order. ${t}`,props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Overrides the grid's accessible name, the locale-formatted month page by default."}]},{name:"YearGrid",lightDom:!0,description:`role="grid" container: twenty gridcells (five rows of four), one twenty-year page. Shows while view is 'year'. Same full grid model and aria-disabled rule as MonthGrid, one unit coarser. ${t}`,props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Overrides the grid's accessible name, the locale-formatted year page by default."}]},{name:"PreviousButton",description:'Button paging the calendar back one unit of the current view, named "Previous month", "Previous year" or "Previous 20 years" by default.',props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Accessible name for the button, replacing the view-dependent default."}]},{name:"NextButton",description:'Button paging the calendar forward one unit of the current view, named "Next month", "Next year" or "Next 20 years" by default.',props:[{name:"label",type:"string",required:!1,frameworks:["angular","webComponents"],exception:"attribute-passthrough",frameworksReason:"React, Vue and Svelte take `aria-label` on the element through attribute passthrough.",description:"Accessible name for the button, replacing the view-dependent default."}]},{name:"Heading",description:"Label for what is on screen and view-up trigger. Renders as button stepping view up when below maxView, otherwise plain label.",props:[{name:"monthOffset",type:"number",defaultValue:"0",required:!1,description:"Which of the displayed months to label, 0 being the first."}],slots:[{name:"default",description:"Displayed month's label; empty for locale-derived default."}]},{name:"Cell",description:'role="gridcell" date button carrying state hooks (selected, today, outside-month, disabled, unavailable, data-range-*).',props:[{name:"date",type:"Date | string",required:!0,description:`The day this cell renders. Required and undefaulted: a cell has no meaning without one, and there is no sensible fallback \u2014 the grid supplies it while iterating a week, so it is the one prop every consumer writes. ${e}`}],slots:[{name:"default",description:"A single selectable date cell."}]}],slots:[{name:"default",description:"Replaces the derived anatomy entirely. Left empty, the calendar draws all of it. Takes the Calendar parts."}],states:["default","selected","today","outside-month","disabled","unavailable","range-start","range-middle","range-end"],controlledUncontrolled:"value, month, focusedDate and view each work controlled or uncontrolled, independently of one another.",formBehavior:"Not applicable \u2014 Calendar does not itself submit; pair with DatePicker for a form-field trigger.",validationBehavior:"Not applicable \u2014 Calendar performs no validation beyond min/max/disabled/unavailable cell rendering. Range and multiple selections are announced through the shared LiveAnnouncer (the anchored start, then the completed range; the running count in multiple mode), because that state lives across several cells and no single aria-selected flip conveys it.",accessibility:{role:'grid (the date grid), rowgroup (GridHead and GridBody), row (each week and the weekday-name row), columnheader (each weekday name, and the week-number column when weekNumbers is on), rowheader (each week number), gridcell (each date). GridHead is a rowgroup, NOT a row, and GridBody is a rowgroup, not a bare wrapper \u2014 a role="row" nested in a role="row", or a plain div between the grid and its rows, is what axe caught as aria-required-children / aria-required-parent in all five frameworks; the implementations were fixed to match.',keyboard:[{key:"ArrowLeft/ArrowRight",behavior:"Move focus by one day."},{key:"ArrowUp/ArrowDown",behavior:"Move focus by one week."},{key:"Home/End",behavior:"Jump to the start/end of the focused week."},{key:"PageUp/PageDown",behavior:"Move focus by one month. Unlike the navigation buttons, which page by pageBehavior, the keyboard always moves a single month: it moves the focused *date*, and paging it by numberOfMonths would jump the cursor rather than the view."},{key:"Shift+PageUp/PageDown",behavior:"Move focus by one year."},{key:"ArrowLeft/ArrowRight (month/year grid)",behavior:"Move focus by one month, or by one year."},{key:"ArrowUp/ArrowDown (month/year grid)",behavior:"Move focus by one row \u2014 four months, or four years, since both grids are four columns wide."},{key:"Home/End (month/year grid)",behavior:"Jump to the first/last cell of the page: January/December, or the twenty-year page's first/last year."},{key:"PageUp/PageDown (month/year grid)",behavior:"Move focus by one whole page \u2014 a year in the month grid, twenty years in the year grid \u2014 and announce the new page, since paging replaces every cell and moves no focus assistive technology would report."},{key:"Shift+PageUp/PageDown (month/year grid)",behavior:"Move focus by ten pages \u2014 ten years, or two hundred."},{key:"Enter/Space (month/year grid)",behavior:"Navigate to the focused month or year and step the view down one level. Selects nothing: only the day grid selects."},{key:"Enter/Space",behavior:"Select the focused date (native button activation). In range mode the first press anchors the range start and the second closes it, ordering start/end so a backwards selection still yields start <= end; a third press starts over."}],ariaAttributes:["aria-selected on every gridcell in the selection \u2014 in range mode that is every date from start to end inclusive, not just the two endpoints",'aria-disabled="true" on an unavailable (but still focusable) gridcell',`aria-current="date" on the gridcell representing today, and on no other cell \u2014 the sighted-only data-today hook is not a substitute, because inside a grid of bare day numbers today is the one part of a cell's meaning that cannot be read off its own text`,"aria-multiselectable on the grid, false only in single-selection mode",'an accessible name on the grid naming its month, from the same Intl formatter the Heading uses \u2014 role="grid" is name-from-author only, so an unnamed one announces as a bare "grid", and with numberOfMonths above 1 there are several identical-sounding grids on screen. Emitted as aria-label and suppressed the moment the consumer supplies an aria-label or aria-labelledby of their own',`aria-label on every gridcell carrying the full localized date ("Monday, January 5, 2026"), from the same Intl formatter the selection announcement uses. A cell's own text is a bare day number, so an unlabelled cell announces as "14" with no month \u2014 which makes arrowing into an adjacent month, and a PageDown that changes it, completely silent. Localized by locale rather than by a prop, because it is derived from the value rather than invented by the widget`,'aria-readonly="true" on the grid while readOnly, and absent otherwise \u2014 a read-only calendar keeps every cell focusable and every tab stop, so this attribute is the only thing that distinguishes it from an editable one','aria-disabled="true" on the grid while the whole calendar is disabled, and absent otherwise \u2014 every cell is natively disabled and therefore silent, so the grid is the only element left to carry the state','an accessible name on MonthGrid and YearGrid too, for the same name-from-author reason the day grid needs one: the focused year ("2026") and the year page ("2020 - 2039"), from the same values the Heading renders, so the two never disagree about what is on screen',`aria-label on every month and year gridcell carrying the full localized unit name ("January 2026", "2026") \u2014 a month cell's own text is an abbreviated month name and a year cell's is four digits, neither of which says which year or which page it belongs to`,'aria-disabled="true" on a month or year gridcell no date can satisfy, which keeps it focusable rather than removing it from the grid \u2014 the unavailable model, not the disabled one, because a twelve-cell grid with silent gaps reads as a broken grid'],focusManagement:"A controlled value that moves to a date outside every displayed month takes the focused date and the displayed month with it, firing month-change \u2014 otherwise the grid keeps its only tab stop on a cell that is no longer rendered, and Tab reaches the calendar to land on nothing. Roving tabindex: only the currently focused date is a tab stop (tabIndex 0), all others -1, and that date is readable and settable as focusedDate with every move reported through focus-change. Unavailable dates keep their place in the roving order; disabled ones do not, and an outside-month day joins it only while outsideDaySelectable is on. A disabled calendar has no tab stop at all, because every cell carries the native disabled attribute. The month and year grids run the same roving model one unit coarser: the tab stop sits on the cell focusedDate falls in \u2014 its month, or its year \u2014 so the cursor survives every step up and down the view stack rather than resetting, and a cell no date can satisfy keeps its place in that order because it is aria-disabled rather than natively disabled.",namingRequirement:'Weekday columnheaders render the locale short name with the full locale name as aria-label, so screen readers say "Monday" rather than "Mon".'},styling:{cssParts:["calendar","header","heading","previous-button","next-button","grid","row","week-day","cell","grid-head","grid-body","month-grid","year-grid","view-cell","week-number","week-number-header"],dataAttributes:[...s,"data-state","data-today","data-outside-month","data-disabled","data-unavailable","data-range-start","data-range-middle","data-range-end","data-skdx-calendar-cell","data-skdx-calendar-view-cell",{name:"data-skdx-calendar-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:[{name:"setValue",signature:"(date: Date) => 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:"Moves the selection to date and emits value-change, exactly as picking that cell would."}],frameworkExtensions:[{framework:"webComponents",part:"Grid",name:"gridMonth",type:"Date | undefined",description:"The month this grid renders, since cells read it instead of the root's month. 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:"valueAsString",type:"string | null",description:"Current selection in wire form, the same as the `value` attribute. 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:"Grid",name:"gridMonth",type:"Date | undefined",description:"The month this grid renders, since cells read it instead of the root's month. 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",name:"valueAsString",type:"string | null",description:"Current selection in wire form, the same as the `value` attribute. 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{t as CALENDAR_PROJECTION_RULE,c as calendarContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"Card",purpose:"A structural container with Media, Header, Title, Description, Content, and Footer parts. The elevation and interactive appearance props (or the data-elevation/data-interactive attributes directly) control styling; selected (or data-selected) marks the chosen card.",props:[{name:"as",type:"PolymorphicTag",defaultValue:"'div'",required:!1,frameworks:["react","vue","svelte","webComponents"],exception:"as-directive",frameworksReason:"Angular has no `as` input. Its element-choice mechanism is an attribute directive the consumer puts on their own element \u2014 `<section skdx-ng-card>` \u2014 which is the same capability spelled the way Angular's template compiler already reads, and eleven such directives predate this one.",description:"Element name to render instead of <div>, from POLYMORPHIC_TAGS. A card that names a region is a <section> with an accessible name. A tag name only, never a component: the list stays closed because Lit needs a static-html literal per tag. CardRoot additionally accepts asChild for component substitution; the Card recipe omits it, since it always wraps its children in derived anatomy."},{name:"title",type:"string",required:!1,description:"The card's heading as a plain string, which the recipe renders into a derived Title inside a derived Header. Composing Header and Title by hand still works and is what rich markup needs; the two reach the same part."},{name:"description",type:"string",required:!1,description:"The card's supporting text as a plain string, rendered into a derived Description inside the same derived Header as `title`."},{name:"headingLevel",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Heading level for the derived Title (h1\u2013h6), mirrored to the Title part's `level`. Without this, derived titles would lock to <h3>, but a card is placed by its consumer so the fitting level varies."},{name:"media",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:"Image or video for a derived Media region at the card's start, clipped to block-start corners. A prop in React, Svelte, and Angular; Vue and Web Components use the `media` slot instead."},{name:"footer",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, for the same reasons `media` does \u2014 Vue idiom on one side, the custom-element attribute limit on the other. Both slot spellings are declared under `slots`.",description:"Actions for a derived Footer at the card's end. A prop in React, Svelte, and Angular; Vue and Web Components use the `footer` slot."},{name:"elevation",type:"'raised' | (string & {})",required:!1,description:"Shadow rung, written to data-elevation on the root; the data-elevation attribute may be set directly instead."},{name:"interactive",type:"boolean",required:!1,description:"Marks the card as clickable, written to data-interactive on the root; the data-interactive attribute may be set directly instead."},{name:"selected",type:"boolean",required:!1,description:"Marks the card as the chosen one of a set, written to data-selected on the root; the data-selected attribute may be set directly instead."}],events:[],parts:[{name:"Media",description:"Edge-to-edge image or video region, the card's *first* child. Clipped to block-start corners so the image reaches the border without escaping the radius."},{name:"Header",description:"Container for the card's Title and Description; renders a <div>."},{name:"Title",description:"The card's heading. Renders <h1>\u2013<h6> as `level` selects; <h3> by default.",props:[{name:"level",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Heading level for the rendered element: `3` renders <h3>. A card is placed by its consumer so the fitting level varies; this is how it fits its outline. React can also reach this through `asChild`."}]},{name:"Description",description:"Supporting text below the title; renders a <p>."},{name:"Content",description:"The card's main body; renders a <div>."},{name:"Footer",description:"Container for actions at the bottom of the card; renders a <div>."}],slots:[{name:"default",description:"Card body (typically Header, Content, Footer). Rendered unwrapped, so consumer-composed Header or Footer is not nested in the body."},{name:"media",description:"Image or video at the card's start."},{name:"header",description:"Title and Description content."},{name:"title",description:"The card's heading."},{name:"description",description:"Supporting text below the title."},{name:"content",description:"The card's main body."},{name:"footer",description:"Actions at the card's end, typically buttons."}],appearance:[{attribute:"data-elevation",values:["raised"],stylesheet:"card.css"},{attribute:"data-interactive",values:[],stylesheet:"card.css"}],states:["default"],controlledUncontrolled:"Stateless \u2014 Card holds no internal state.",formBehavior:"Not applicable \u2014 Card participates in no form.",validationBehavior:"Not applicable \u2014 Card carries no value and participates in no validation.",accessibility:{role:"generic (native <div>/<h1>\u2013<h6>/<p> elements, no implicit landmark role)",keyboard:[],ariaAttributes:["data-interactive is valid only when the root is an <a href> or <button> (reachable through the as prop, CardRoot's asChild, or by the consumer replacing the root). The skin adds pointer cursor and hover; on a plain <div> the attribute promises a click nothing delivers.",'data-selected requires aria-selected="true" (in a listbox or grid) or aria-current (for the active item in a set). Colour alone does not carry state; under forced-colors the selected border collapses.'],focusManagement:"Not focusable unless its root is an <a href> or <button>, which makes it focusable by their nature, not by Card adding a tabindex."},styling:{cssParts:["card","media","header","title","description","content","footer"],dataAttributes:[...e,"data-elevation","data-interactive","data-selected",{name:"data-skdx-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:["--skdx-card-padding","--skdx-card-radius"]},publicMethods:[]};export{a as cardContract};
|
|
1
|
+
import{ROOT_DATA_ATTRIBUTES as e}from"./styling-hooks.js";var a={name:"Card",purpose:"A structural container with Media, Header, Title, Description, Content, and Footer parts. The elevation and interactive appearance props (or the data-elevation/data-interactive attributes directly) control styling; selected (or data-selected) marks the chosen card.",props:[{name:"as",type:"PolymorphicTag",defaultValue:"'div'",required:!1,frameworks:["react","vue","svelte","webComponents"],exception:"as-directive",frameworksReason:"Angular has no `as` input. Its element-choice mechanism is an attribute directive the consumer puts on their own element \u2014 `<section skdx-ng-card>` \u2014 which is the same capability spelled the way Angular's template compiler already reads, and eleven such directives predate this one.",description:"Element name to render instead of <div>, from POLYMORPHIC_TAGS. A card that names a region is a <section> with an accessible name. A tag name only, never a component: the list stays closed because Lit needs a static-html literal per tag. CardRoot additionally accepts asChild for component substitution; the Card recipe omits it, since it always wraps its children in derived anatomy."},{name:"title",type:"string",required:!1,description:"The card's heading as a plain string, which the recipe renders into a derived Title inside a derived Header. Composing Header and Title by hand still works and is what rich markup needs; the two reach the same part."},{name:"description",type:"string",required:!1,description:"The card's supporting text as a plain string, rendered into a derived Description inside the same derived Header as `title`."},{name:"headingLevel",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Heading level for the derived Title (h1\u2013h6), mirrored to the Title part's `level`. Without this, derived titles would lock to <h3>, but a card is placed by its consumer so the fitting level varies."},{name:"media",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:"Image or video for a derived Media region at the card's start, clipped to block-start corners. A prop in React, Svelte, and Angular; Vue and Web Components use the `media` slot instead."},{name:"footer",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, for the same reasons `media` does \u2014 Vue idiom on one side, the custom-element attribute limit on the other. Both slot spellings are declared under `slots`.",description:"Actions for a derived Footer at the card's end. A prop in React, Svelte, and Angular; Vue and Web Components use the `footer` slot."},{name:"elevation",type:"'raised' | (string & {})",required:!1,description:"Shadow rung, written to data-elevation on the root; the data-elevation attribute may be set directly instead."},{name:"interactive",type:"boolean",required:!1,description:"Marks the card as clickable, written to data-interactive on the root; the data-interactive attribute may be set directly instead."},{name:"selected",type:"boolean",required:!1,description:"Marks the card as the chosen one of a set, written to data-selected on the root; the data-selected attribute may be set directly instead."}],events:[],parts:[{name:"Media",description:"Edge-to-edge image or video region, the card's *first* child. Clipped to block-start corners so the image reaches the border without escaping the radius."},{name:"Header",description:"Container for the card's Title and Description; renders a <div>.",slots:[{name:"default",description:"Title and Description content."}]},{name:"Title",description:"The card's heading. Renders <h1>\u2013<h6> as `level` selects; <h3> by default.",props:[{name:"level",type:"1 | 2 | 3 | 4 | 5 | 6",defaultValue:"3",required:!1,description:"Heading level for the rendered element: `3` renders <h3>. A card is placed by its consumer so the fitting level varies; this is how it fits its outline. React can also reach this through `asChild`."}],slots:[{name:"default",description:"The card's heading."}]},{name:"Description",description:"Supporting text below the title; renders a <p>.",slots:[{name:"default",description:"Supporting text below the title."}]},{name:"Content",description:"The card's main body; renders a <div>.",slots:[{name:"default",description:"The card's main body."}]},{name:"Footer",description:"Container for actions at the bottom of the card; renders a <div>."}],slots:[{name:"default",description:"Card body (typically Header, Content, Footer). Rendered unwrapped, so consumer-composed Header or Footer is not nested in the body."},{name:"media",description:"Image or video at the card's start."},{name:"header",description:"Title and Description content.",frameworks:["vue"],exception:"derived-anatomy",frameworksReason:"The root exposes header as content projection in vue. Other wrappers project default content into the Header part or use their separately documented rendering property; they do not accept a root named slot with this spelling."},{name:"title",description:"The card's heading.",frameworks:["vue"],exception:"derived-anatomy",frameworksReason:"The root exposes title as content projection in vue. Other wrappers project default content into the Title part or use their separately documented rendering property; they do not accept a root named slot with this spelling."},{name:"description",description:"Supporting text below the title.",frameworks:["vue"],exception:"derived-anatomy",frameworksReason:"The root exposes description as content projection in vue. Other wrappers project default content into the Description part or use their separately documented rendering property; they do not accept a root named slot with this spelling."},{name:"content",description:"The card's main body.",frameworks:["vue"],exception:"derived-anatomy",frameworksReason:"The root exposes content as content projection in vue. Other wrappers project default content into the Content part or use their separately documented rendering property; they do not accept a root named slot with this spelling."},{name:"footer",description:"Actions at the card's end, typically buttons."}],appearance:[{attribute:"data-elevation",values:["raised"],stylesheet:"card.css"},{attribute:"data-interactive",values:[],stylesheet:"card.css"}],states:["default"],controlledUncontrolled:"Stateless \u2014 Card holds no internal state.",formBehavior:"Not applicable \u2014 Card participates in no form.",validationBehavior:"Not applicable \u2014 Card carries no value and participates in no validation.",accessibility:{role:"generic (native <div>/<h1>\u2013<h6>/<p> elements, no implicit landmark role)",keyboard:[],ariaAttributes:["data-interactive is valid only when the root is an <a href> or <button> (reachable through the as prop, CardRoot's asChild, or by the consumer replacing the root). The skin adds pointer cursor and hover; on a plain <div> the attribute promises a click nothing delivers.",'data-selected requires aria-selected="true" (in a listbox or grid) or aria-current (for the active item in a set). Colour alone does not carry state; under forced-colors the selected border collapses.'],focusManagement:"Not focusable unless its root is an <a href> or <button>, which makes it focusable by their nature, not by Card adding a tabindex."},styling:{cssParts:["card","media","header","title","description","content","footer"],dataAttributes:[...e,"data-elevation","data-interactive","data-selected",{name:"data-skdx-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:["--skdx-card-padding","--skdx-card-radius"]},publicMethods:[]};export{a as cardContract};
|