@primitiv-ui/web 0.1.52 → 0.1.54

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/README.md CHANGED
@@ -6,7 +6,7 @@ framework and no runtime dependencies beyond
6
6
  server-rendered stack (HTMX, Rails, Django, Laravel, Spring …), and inside
7
7
  React, Vue, Angular or any other framework.
8
8
 
9
- > **Status: published.** Tabs, Accordion and Collapsible so far. See
9
+ > **Status: published.** Tabs, Accordion, Collapsible and Modal so far. See
10
10
  > [RFC 0033](../../docs/rfcs/0033-web-components.md) for the design.
11
11
 
12
12
  ## Components
@@ -16,6 +16,7 @@ React, Vue, Angular or any other framework.
16
16
  | Tabs | `primitiv-tabs`, `-list`, `-trigger`, `-content` | `primitiv-tabs-change` | 0.1.42 |
17
17
  | Accordion | `primitiv-accordion`, `-item`, `-trigger`, `-content`, `-trigger-icon` | `primitiv-accordion-change` | 0.1.44 |
18
18
  | Collapsible | `primitiv-collapsible`, `-trigger`, `-content`, `-trigger-icon` | `primitiv-collapsible-change` | 0.1.47 |
19
+ | Modal | `primitiv-modal`, `-trigger`, `-content`, `-title`, `-description`, `-close` | `primitiv-modal-change` | 0.1.54 |
19
20
 
20
21
  ## Use it
21
22
 
@@ -47,9 +48,10 @@ Each `dist/cdn/<component>.js` imports the code components share from
47
48
  downloads it once. `dist/primitiv-web.js` is the alternative: every element in
48
49
  one file. Per-component files ship from 0.1.43.
49
50
 
50
- The main entry (`@primitiv-ui/web`, `/tabs`, `/accordion`, `/collapsible`)
51
- exports the classes and `defineTabs(registry?)` / `defineAccordion(registry?)` /
52
- `defineCollapsible(registry?)` with **no side effects**.
51
+ The main entry (`@primitiv-ui/web`, `/tabs`, `/accordion`, `/collapsible`,
52
+ `/modal`) exports the classes and `defineTabs(registry?)` /
53
+ `defineAccordion(registry?)` / `defineCollapsible(registry?)` /
54
+ `defineModal(registry?)` with **no side effects**.
53
55
 
54
56
  **Render `hidden` on inactive panels yourself.** Until the module loads, CSS
55
57
  cannot tell which panel is active, so markup that omits it shows every panel.
@@ -118,6 +120,40 @@ on a panel as it becomes active, for lazy loading.
118
120
  open }`) and `primitiv-collapsible-show` on the panel as it opens. Render
119
121
  `hidden` on a closed panel yourself, as for Tabs.
120
122
 
123
+ ### Modal
124
+
125
+ ```html
126
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@primitiv-ui/web@<version>/dist/cdn/modal.js"></script>
127
+
128
+ <primitiv-modal>
129
+ <primitiv-modal-trigger><button type="button">Edit profile</button></primitiv-modal-trigger>
130
+ <primitiv-modal-content>
131
+ <dialog>
132
+ <primitiv-modal-title><h2>Edit profile</h2></primitiv-modal-title>
133
+ <primitiv-modal-description><p>Changes apply to every workspace.</p></primitiv-modal-description>
134
+ <primitiv-modal-close><button type="button">Done</button></primitiv-modal-close>
135
+ </dialog>
136
+ </primitiv-modal-content>
137
+ </primitiv-modal>
138
+ ```
139
+
140
+ - **It is a native `<dialog>`, opened with `showModal()`**: the browser supplies
141
+ the top layer, the `::backdrop` and the inert page. The element adds the
142
+ Tab wrap, closing on Escape and on a backdrop press, and the ARIA wiring —
143
+ `aria-haspopup`, `aria-expanded` and `aria-controls` on the trigger's button,
144
+ `aria-labelledby` / `aria-describedby` from the title's heading and the
145
+ description's paragraph. Focus returns to the trigger on close.
146
+ - **`open` on the root is the state**, reflected as the user opens and dismisses
147
+ it; the `open` property reads and sets it, and setting it is not announced.
148
+ Leave `open` off the `<dialog>` itself — the root opens it modally, and
149
+ reopens modally one that arrives with it (a history snapshot).
150
+ - **Several triggers and close buttons** are fine. There is no overlay element:
151
+ style `::backdrop`.
152
+ - **Listen with `primitiv-modal-change`** (bubbles, cancelable, `detail: { open
153
+ }`), fired before every user-driven change — trigger, close, Escape, a backdrop
154
+ press, a `<form method="dialog">` — so `preventDefault()` keeps a dirty form
155
+ open.
156
+
121
157
  **TypeScript** — opt into the tag map so DOM queries return the element classes:
122
158
  `import type {} from "@primitiv-ui/web/tags"` (npm only).
123
159
 
@@ -1 +1 @@
1
- import{a as b,e as C,f as O,i as g,j as n,k as v,l as H,m as f,n as R,o as M}from"./chunks/chunk-WVI5UEAW.js";var m=class extends M{#t;constructor(e,s){super(e),this.#t=s}#e(e){let{expanded:s,multiple:c}=this.#t.state(),r=C({disabled:this.disabled(),expanded:s,multiple:c},e);r!==null&&this.#t.change(r)}onTriggerClick(e){this.#e(e.value)}onTriggerKeydown(e,s){let{expanded:c,multiple:r,orientation:l,dir:d}=this.#t.state(),u=this.ordered(),p=u.map(([,o])=>o.value),t=O({values:p,disabled:this.disabled(),expanded:c,multiple:r,orientation:l,dir:d},e.value,s.key);t&&(s.preventDefault(),t.toggle!==null&&this.#e(t.toggle),t.focus!==null&&u[p.indexOf(t.focus)][0].focus())}};var k="primitiv-accordion",h="primitiv-accordion-item",w="primitiv-accordion-trigger",L="primitiv-accordion-content",S="primitiv-accordion-trigger-icon";function D(i){return(i??"").split(/\s+/).filter(Boolean)}function K(i){return i.childElementCount===1?i.firstElementChild:(v(i,`<${w}> needs exactly one element child (a <button>) to decorate.`),null)}var y=class extends R{#t=new Set;#e=!1;controller=new m(this,this.#i());reconcileOn=["value","disabled","orientation","force-mount"];get value(){return D(this.getAttribute("value"))}set value(e){this.setAttribute("value",typeof e=="string"?e:e.join(" "))}#i(){return{state:()=>({expanded:this.value,multiple:this.hasAttribute("multiple"),orientation:this.#o(),dir:this.direction()}),change:e=>{this.announce("primitiv-accordion-change",{value:e})&&(this.value=e)}}}#o(){return this.getAttribute("orientation")==="horizontal"?"horizontal":"vertical"}reconcile(){let e=new Set(this.value),s=this.rootId();n(this,"data-orientation",this.#o());let c=[];for(let r of this.parts(h)){let l=r.getAttribute("value");if(l===null){v(r,`<${h}> needs a value, which pairs it with the root's value.`);continue}let d=e.has(l),u=d?"open":"closed",[p]=f(r,w,h),[t]=f(r,L,h),o=p?K(p):null;if(o){let a=p.hasAttribute("disabled");c.push({value:l,element:o,disabled:a}),o.id||(o.id=b(s,"heading",l)),n(o,"aria-expanded",String(d)),n(o,"aria-disabled",String(a)),n(o,"data-disabled",String(a)),n(o,"data-state",u)}if(t){t.id||(t.id=b(s,"panel",l));let a=t.hasAttribute("force-mount");t.hasAttribute("role")||t.setAttribute("role","region"),n(t,"aria-labelledby",o?o.id:null),n(t,"hidden",d||a?null:""),n(t,"aria-hidden",d?null:"true"),n(t,"data-state",u),this.#e&&d&&!this.#t.has(l)&&t.dispatchEvent(new CustomEvent("primitiv-accordion-show",{bubbles:!0}))}o&&n(o,"aria-controls",t?t.id:null);for(let a of f(r,S,h))n(a,"aria-hidden","true"),n(a,"data-state",u)}this.controller.setTriggers(c),this.#t=e,this.#e=!0}},x=class extends g{},A=class extends g{},T=class extends g{},E=class extends g{};function I(i=globalThis.customElements){H(i,[[k,y],[h,x],[w,A],[L,T],[S,E]])}I();
1
+ import{a as b,b as R,c as H}from"./chunks/chunk-HXGMU6SD.js";import{a as S}from"./chunks/chunk-EDLRMP2K.js";import{a as h,b as r,c as v,d as O,e as f,f as C}from"./chunks/chunk-QVNDDONZ.js";function K(e,t){return e.disabled.has(t)?null:e.expanded.includes(t)?e.expanded.filter(n=>n!==t):e.multiple?[...e.expanded,t]:[t]}function M(e,t,n){let d=R({orientation:e.orientation,dir:e.dir,homeEnd:!0,activate:!0})[n];if(d==="activate")return e.disabled.has(t)?null:{focus:null,toggle:t};let l=e.values.filter(a=>!e.disabled.has(a)),s=H({action:d,navigable:l,currentKey:t});return s&&{focus:s.target,toggle:null}}var m=class extends S{#t;constructor(t,n){super(t),this.#t=n}#e(t){let{expanded:n,multiple:d}=this.#t.state(),l=K({disabled:this.disabled(),expanded:n,multiple:d},t);l!==null&&this.#t.change(l)}onTriggerClick(t){this.#e(t.value)}onTriggerKeydown(t,n){let{expanded:d,multiple:l,orientation:s,dir:a}=this.#t.state(),u=this.ordered(),p=u.map(([,i])=>i.value),o=M({values:p,disabled:this.disabled(),expanded:d,multiple:l,orientation:s,dir:a},t.value,n.key);o&&(n.preventDefault(),o.toggle!==null&&this.#e(o.toggle),o.focus!==null&&u[p.indexOf(o.focus)][0].focus())}};var I="primitiv-accordion",g="primitiv-accordion-item",w="primitiv-accordion-trigger",L="primitiv-accordion-content",k="primitiv-accordion-trigger-icon";function z(e){return(e??"").split(/\s+/).filter(Boolean)}function N(e){return e.childElementCount===1?e.firstElementChild:(v(e,`<${w}> needs exactly one element child (a <button>) to decorate.`),null)}var y=class extends C{#t=new Set;#e=!1;controller=new m(this,this.#i());reconcileOn=["value","disabled","orientation","force-mount"];get value(){return z(this.getAttribute("value"))}set value(t){this.setAttribute("value",typeof t=="string"?t:t.join(" "))}#i(){return{state:()=>({expanded:this.value,multiple:this.hasAttribute("multiple"),orientation:this.#o(),dir:this.direction()}),change:t=>{this.announce("primitiv-accordion-change",{value:t})&&(this.value=t)}}}#o(){return this.getAttribute("orientation")==="horizontal"?"horizontal":"vertical"}reconcile(){let t=new Set(this.value),n=this.rootId();r(this,"data-orientation",this.#o());let d=[];for(let l of this.parts(g)){let s=l.getAttribute("value");if(s===null){v(l,`<${g}> needs a value, which pairs it with the root's value.`);continue}let a=t.has(s),u=a?"open":"closed",[p]=f(l,w,g),[o]=f(l,L,g),i=p?N(p):null;if(i){let c=p.hasAttribute("disabled");d.push({value:s,element:i,disabled:c}),i.id||(i.id=b(n,"heading",s)),r(i,"aria-expanded",String(a)),r(i,"aria-disabled",String(c)),r(i,"data-disabled",String(c)),r(i,"data-state",u)}if(o){o.id||(o.id=b(n,"panel",s));let c=o.hasAttribute("force-mount");o.hasAttribute("role")||o.setAttribute("role","region"),r(o,"aria-labelledby",i?i.id:null),r(o,"hidden",a||c?null:""),r(o,"aria-hidden",a?null:"true"),r(o,"data-state",u),this.#e&&a&&!this.#t.has(s)&&o.dispatchEvent(new CustomEvent("primitiv-accordion-show",{bubbles:!0}))}i&&r(i,"aria-controls",o?o.id:null);for(let c of f(l,k,g))r(c,"aria-hidden","true"),r(c,"data-state",u)}this.controller.setTriggers(d),this.#t=t,this.#e=!0}},x=class extends h{},A=class extends h{},T=class extends h{},E=class extends h{};function D(e=globalThis.customElements){O(e,[[I,y],[g,x],[w,A],[L,T],[k,E]])}D();
@@ -0,0 +1 @@
1
+ var n=class{#r;#e=new Map;#t=null;constructor(e){this.#r=e}registerTrigger(e,t,r={}){this.#e.set(t,{value:e,disabled:r.disabled??!1})}setTriggers(e){this.#e.clear();for(let{value:t,element:r,disabled:i}of e)this.registerTrigger(t,r,{disabled:i})}unregisterTrigger(e){this.#e.delete(e)}connect(){if(this.#t)return;this.#t=new AbortController;let{signal:e}=this.#t;this.#r.addEventListener("click",t=>this.#i(t,this.onTriggerClick),{signal:e}),this.#r.addEventListener("keydown",t=>this.#i(t,this.onTriggerKeydown),{signal:e})}disconnect(){this.#t?.abort(),this.#t=null}ordered(){return[...this.#e].sort(([e],[t])=>e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING?-1:1)}disabled(){let e=new Set;for(let t of this.#e.values())t.disabled&&e.add(t.value);return e}#i(e,t){for(let[r,i]of this.#e)if(r.contains(e.target))return t.call(this,i,e)}};export{n as a};
@@ -0,0 +1 @@
1
+ function c(r,e,n){return`${r}-${e}-${n}`}function l({orientation:r,dir:e,homeEnd:n=!1,activate:o=!1}){let t={};if(r==="horizontal"||r==="both"){let i=e==="rtl"?"ArrowLeft":"ArrowRight",a=e==="rtl"?"ArrowRight":"ArrowLeft";t[i]="next",t[a]="prev"}return(r==="vertical"||r==="both")&&(t.ArrowDown="next",t.ArrowUp="prev"),n&&(t.Home="first",t.End="last"),o&&(t.Enter="activate",t[" "]="activate"),t}function g({action:r,navigable:e,currentKey:n}){let o=e.length;if(o===0)return null;switch(r){case"activate":return{target:n};case"first":return{target:e[0]};case"last":return{target:e[o-1]};case void 0:return null;case"next":case"prev":{let t=e.indexOf(n);if(t===-1)return null;let i=r==="next"?1:o-1;return{target:e[(t+i)%o]}}}}export{c as a,l as b,g as c};
@@ -0,0 +1 @@
1
+ var i=globalThis.HTMLElement??class{};function d(e,t,n){e.getAttribute(t)!==n&&(n===null?e.removeAttribute(t):e.setAttribute(t,n))}var o=new WeakSet;function a(e,t){o.has(e)||(o.add(e),console.error(t,e))}function u(e,t){if(e)for(let[n,r]of t)e.get(n)||e.define(n,r)}function s(e,t,n){return[...e.querySelectorAll(t)].filter(r=>r.closest(n)===e)}var l=0,c=class extends i{#t;#e=new MutationObserver(()=>this.reconcile());connectedCallback(){this.controller.connect(),this.#e.observe(this,{subtree:!0,childList:!0,attributes:!0,attributeFilter:[...this.reconcileOn]}),this.reconcile()}disconnectedCallback(){this.controller.disconnect(),this.#e.disconnect()}rootId(){return this.id||(this.#t??=`${this.localName}-${++l}`)}direction(){return this.matches(":dir(rtl)")?"rtl":"ltr"}parts(t){return s(this,t,this.localName)}announce(t,n){return this.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n}))}};export{i as a,d as b,a as c,u as d,s as e,c as f};
@@ -1 +1 @@
1
- import{g as y,h as T,i as d,j as i,k as x,l as C,n as E,o as v}from"./chunks/chunk-WVI5UEAW.js";var p=class extends v{#t;constructor(t,o){super(t),this.#t=o}onTriggerClick(t){let o=y({open:this.#t.state().open,disabled:t.disabled});o!==null&&this.#t.change(o)}onTriggerKeydown(t,o){let s=T({open:this.#t.state().open,disabled:t.disabled},o.key);s&&(o.preventDefault(),this.#t.change(s.open))}};var R="primitiv-collapsible",b="primitiv-collapsible-trigger",H="primitiv-collapsible-content",w="primitiv-collapsible-trigger-icon",L="--primitiv-collapsible-collapsed-height";function O(e){return e.childElementCount===1?e.firstElementChild:(x(e,`<${b}> needs exactly one element child (a <button>) to decorate.`),null)}function M(e){return Number.isNaN(Number(e))?e:`${e}px`}function I(e,t,o){e.style.getPropertyValue(t)!==o&&(e.style.setProperty(t,o),e.getAttribute("style")===""&&e.removeAttribute("style"))}var c=class extends E{#t;controller=new p(this,this.#e());reconcileOn=["open","disabled","force-mount","collapsed-height"];get open(){return this.hasAttribute("open")}set open(t){this.toggleAttribute("open",t)}#e(){return{state:()=>({open:this.open}),change:t=>{this.announce("primitiv-collapsible-change",{open:t})&&(this.open=t)}}}reconcile(){let t=this.open,o=t?"open":"closed",s=this.hasAttribute("disabled"),m=this.rootId();i(this,"data-state",o),i(this,"data-disabled",String(s));let[f]=this.parts(b),[n]=this.parts(H),r=f?O(f):null;if(r&&(r.id||(r.id=`${m}-trigger`),i(r,"aria-expanded",String(t)),i(r,"aria-disabled",s?"true":null),i(r,"data-disabled",String(s)),i(r,"data-state",o)),n){n.id||(n.id=`${m}-content`);let l=n.getAttribute("collapsed-height"),a=l!==null,N=a||n.hasAttribute("force-mount");i(n,"hidden",t||N?null:""),i(n,"aria-hidden",t||a?null:"true"),i(n,"data-state",o),i(n,"data-disabled",String(s)),i(n,"data-clamped",a?"true":null),I(n,L,a?M(l):""),t&&this.#t===!1&&n.dispatchEvent(new CustomEvent("primitiv-collapsible-show",{bubbles:!0}))}r&&i(r,"aria-controls",n?n.id:null);for(let l of this.parts(w))i(l,"aria-hidden","true"),i(l,"data-state",o);this.controller.setTriggers(r?[{value:"trigger",element:r,disabled:s}]:[]),this.#t=t}},h=class extends d{},u=class extends d{},g=class extends d{};function A(e=globalThis.customElements){C(e,[[R,c],[b,h],[H,u],[w,g]])}A();
1
+ import{a as E}from"./chunks/chunk-EDLRMP2K.js";import{a as p,b as l,c as C,d as x,f as T}from"./chunks/chunk-QVNDDONZ.js";function c(e){return e.disabled?null:!e.open}function v(e,t){if(t!=="Enter"&&t!==" ")return null;let o=c(e);return o===null?null:{open:o}}var d=class extends E{#e;constructor(t,o){super(t),this.#e=o}onTriggerClick(t){let o=c({open:this.#e.state().open,disabled:t.disabled});o!==null&&this.#e.change(o)}onTriggerKeydown(t,o){let r=v({open:this.#e.state().open,disabled:t.disabled},o.key);r&&(o.preventDefault(),this.#e.change(r.open))}};var N="primitiv-collapsible",f="primitiv-collapsible-trigger",H="primitiv-collapsible-content",w="primitiv-collapsible-trigger-icon",R="--primitiv-collapsible-collapsed-height";function S(e){return e.childElementCount===1?e.firstElementChild:(C(e,`<${f}> needs exactly one element child (a <button>) to decorate.`),null)}function K(e){return Number.isNaN(Number(e))?e:`${e}px`}function L(e,t,o){e.style.getPropertyValue(t)!==o&&(e.style.setProperty(t,o),e.getAttribute("style")===""&&e.removeAttribute("style"))}var u=class extends T{#e;controller=new d(this,this.#t());reconcileOn=["open","disabled","force-mount","collapsed-height"];get open(){return this.hasAttribute("open")}set open(t){this.toggleAttribute("open",t)}#t(){return{state:()=>({open:this.open}),change:t=>{this.announce("primitiv-collapsible-change",{open:t})&&(this.open=t)}}}reconcile(){let t=this.open,o=t?"open":"closed",r=this.hasAttribute("disabled"),m=this.rootId();l(this,"data-state",o),l(this,"data-disabled",String(r));let[y]=this.parts(f),[n]=this.parts(H),i=y?S(y):null;if(i&&(i.id||(i.id=`${m}-trigger`),l(i,"aria-expanded",String(t)),l(i,"aria-disabled",r?"true":null),l(i,"data-disabled",String(r)),l(i,"data-state",o)),n){n.id||(n.id=`${m}-content`);let s=n.getAttribute("collapsed-height"),a=s!==null,A=a||n.hasAttribute("force-mount");l(n,"hidden",t||A?null:""),l(n,"aria-hidden",t||a?null:"true"),l(n,"data-state",o),l(n,"data-disabled",String(r)),l(n,"data-clamped",a?"true":null),L(n,R,a?K(s):""),t&&this.#e===!1&&n.dispatchEvent(new CustomEvent("primitiv-collapsible-show",{bubbles:!0}))}i&&l(i,"aria-controls",n?n.id:null);for(let s of this.parts(w))l(s,"aria-hidden","true"),l(s,"data-state",o);this.controller.setTriggers(i?[{value:"trigger",element:i,disabled:r}]:[]),this.#e=t}},b=class extends p{},h=class extends p{},g=class extends p{};function O(e=globalThis.customElements){x(e,[[N,u],[f,b],[H,h],[w,g]])}O();
@@ -0,0 +1 @@
1
+ import{a as l,b as i,c as h,d as L,f as M}from"./chunks/chunk-QVNDDONZ.js";var C=["a[href]","button:not([disabled])","input:not([disabled])","select:not([disabled])","textarea:not([disabled])",'[tabindex]:not([tabindex="-1"])'].join(",");function H({key:n,shiftKey:t,active:e,count:o}){if(n!=="Tab")return null;if(o===0)return{focus:null};let r=o-1;return!t&&e===r?{focus:0}:t&&e===0?{focus:r}:null}function O(n,t){return t.x<n.left||t.x>n.right||t.y<n.top||t.y>n.bottom}var d=class{#e;#t;#n=null;constructor(t,e){this.#e=t,this.#t=e}connect(){if(this.#n)return;this.#n=new AbortController;let{signal:t}=this.#n,e=this.#e;e.addEventListener("close",()=>this.#t.change(!1),{signal:t}),e.addEventListener("cancel",o=>this.#i(o),{signal:t}),e.addEventListener("pointerdown",o=>this.#o(o),{signal:t}),e.addEventListener("keydown",o=>this.#s(o),{signal:t})}disconnect(){this.#n?.abort(),this.#n=null}sync(){let{open:t}=this.#t.state();t&&!this.#e.open?this.#e.showModal():!t&&this.#e.open&&this.#e.close()}#i(t){this.#t.escapeKeyDown?.(t);let e=t.defaultPrevented;t.preventDefault(),e||this.#t.change(!1)}#o(t){let e={x:t.clientX,y:t.clientY};O(this.#e.getBoundingClientRect(),e)&&(this.#t.pointerDownOutside?.(t),t.defaultPrevented||this.#t.change(!1))}#s(t){if(!this.#e.open)return;let e=[...this.#e.querySelectorAll(C)],o=H({key:t.key,shiftKey:t.shiftKey,active:e.indexOf(document.activeElement),count:e.length});o&&(t.preventDefault(),o.focus!==null&&e[o.focus].focus())}};var k="primitiv-modal",w="primitiv-modal-trigger",v="primitiv-modal-content",D="primitiv-modal-title",R="primitiv-modal-description",A="primitiv-modal-close";function p(n,t){return n.childElementCount===1?n.firstElementChild:(h(n,`<${n.localName}> needs exactly one element child (${t}) to decorate.`),null)}function K(n){let t=n.firstElementChild;return n.childElementCount===1&&t instanceof HTMLDialogElement?t:(h(n,`<${v}> needs exactly one <dialog> child to decorate.`),null)}var f=class extends M{#e=null;#t=null;#n=[];#i=[];#o=null;controller={connect:()=>this.#a(),disconnect:()=>this.#c()};reconcileOn=["open"];get open(){return this.hasAttribute("open")}set open(t){this.toggleAttribute("open",t)}#s(){return{state:()=>({open:this.open}),change:t=>this.#l(t)}}#l(t){t!==this.open&&(this.announce("primitiv-modal-change",{open:t})?this.open=t:this.#t?.sync())}#a(){this.#o=new AbortController,this.addEventListener("click",t=>this.#d(t),{signal:this.#o.signal}),this.#t?.connect()}#c(){this.#o.abort(),this.#o=null,this.#t?.disconnect(),this.#e&&i(this.#e,"open",null)}#d(t){let e=t.target;this.#n.some(o=>o.contains(e))?this.#l(!0):this.#i.some(o=>o.contains(e))&&this.#l(!1)}#u(t){t!==this.#e&&(this.#t?.disconnect(),t&&i(t,"open",null),this.#e=t,this.#t=t?new d(t,this.#s()):null,this.#t?.connect())}reconcile(){let t=this.open,e=t?"open":"closed",o=this.rootId();i(this,"data-state",e);let[r]=this.parts(v),s=r?K(r):null,[x]=this.parts(D),a=x?p(x,"a heading"):null,[T]=this.parts(R),c=T?p(T,"a <p>"):null;a&&!a.id&&(a.id=`${o}-title`),c&&!c.id&&(c.id=`${o}-description`),s&&(s.id||(s.id=`${o}-content`),i(s,"data-state",e),i(s,"aria-labelledby",a?a.id:null),i(s,"aria-describedby",c?c.id:null)),this.#n=this.#r(w);for(let u of this.#n)i(u,"aria-haspopup","dialog"),i(u,"aria-expanded",String(t)),i(u,"aria-controls",s?s.id:null);this.#i=this.#r(A),this.#u(s),this.#t?.sync()}#r(t){return this.parts(t).flatMap(e=>p(e,"a <button>")??[])}},m=class extends l{},b=class extends l{},E=class extends l{},g=class extends l{},y=class extends l{};function S(n=globalThis.customElements){L(n,[[k,f],[w,m],[v,b],[D,E],[R,g],[A,y]])}S();
package/dist/cdn/tabs.js CHANGED
@@ -1 +1 @@
1
- import{a as h,b as M,c as A,d as C,i as b,j as a,k as O,l as H,n as w,o as R}from"./chunks/chunk-WVI5UEAW.js";var v=class extends R{#t;constructor(i,n){super(i),this.#t=n}onTriggerClick(i){let{active:n}=this.#t.state(),r=A({disabled:this.disabled(),active:n},i.value);if(r!==null){let l=this.ordered().map(([,d])=>d.value);this.#t.activate(r,l.indexOf(r))}}onTriggerKeydown(i,n){let{active:r,orientation:l,dir:d,activationMode:p}=this.#t.state(),u=this.ordered(),e=u.map(([,o])=>o.value),t=C({values:e,disabled:this.disabled(),active:r,orientation:l,dir:d,activationMode:p},i.value,n.key);t&&(n.preventDefault(),t.activate!==null&&this.#t.activate(t.activate,e.indexOf(t.activate)),u[e.indexOf(t.focus)][0].focus())}};var D="primitiv-tabs",I="primitiv-tabs-list",y="primitiv-tabs-trigger",L="primitiv-tabs-content";function K(s){let i=s.getAttribute("value");return s.childElementCount===1&&i!==null?{tab:s.firstElementChild,value:i}:(O(s,`<${y}> needs a value and exactly one element child (a <button>, an <a href> or a router link) to decorate.`),null)}var f=class extends w{#t;#e=!1;controller=new v(this,this.#a());reconcileOn=["value","disabled","label","orientation"];get value(){return this.getAttribute("value")??void 0}set value(i){this.setAttribute("value",i)}#a(){return{state:()=>({active:this.value,orientation:this.#i(),dir:this.direction(),activationMode:this.#n()}),activate:(i,n)=>{this.announce("primitiv-tabs-change",{value:i,index:n})&&(this.value=i)}}}#n(){let i=this.getAttribute("activation-mode");return i==="manual"?i:"automatic"}#i(){return this.getAttribute("orientation")==="vertical"?"vertical":"horizontal"}reconcile(){let i=this.#i(),n=this.value,r=this.rootId();a(this,"data-orientation",i);for(let e of this.parts(I)){a(e,"role","tablist"),a(e,"aria-orientation",i),a(e,"data-orientation",i);let t=e.getAttribute("label");t!==null&&a(e,"aria-label",t)}let l=new Map;for(let e of this.parts(L)){let t=e.getAttribute("value");t!==null&&l.set(t,e)}let d=this.parts(y).flatMap(e=>{let t=K(e);return t?[{trigger:e,...t}]:[]}),p=M({values:d.map(e=>e.value),active:n});this.controller.setTriggers(d.map(({trigger:e,tab:t,value:o})=>({value:o,element:t,disabled:e.hasAttribute("disabled")})));for(let{trigger:e,tab:t,value:o}of d){let c=l.get(o),x=o===n,E=e.hasAttribute("disabled");t.id||(t.id=h(r,"trigger",o)),c&&!c.id&&(c.id=h(r,"panel",o)),a(t,"role","tab"),a(t,"aria-selected",String(x)),a(t,"aria-controls",c?c.id:null),a(t,"tabindex",o===p?"0":"-1"),a(t,"aria-disabled",E?"true":null),a(t,"data-disabled",E?"":null),a(t,"data-state",x?"active":"inactive"),a(t,"data-orientation",i),c&&a(c,"aria-labelledby",t.id)}for(let[e,t]of l){let o=e===n;a(t,"role","tabpanel"),a(t,"tabindex","0"),a(t,"hidden",o?null:""),a(t,"data-state",o?"active":"inactive"),a(t,"data-orientation",i)}let u=l.get(n);this.#e&&n!==this.#t&&u&&u.dispatchEvent(new CustomEvent("primitiv-tabs-show",{bubbles:!0})),this.#t=n,this.#e=!0}},m=class extends b{},g=class extends b{},T=class extends b{};function k(s=globalThis.customElements){H(s,[[D,f],[I,m],[y,g],[L,T]])}k();
1
+ import{a as f,b as R,c as w}from"./chunks/chunk-HXGMU6SD.js";import{a as H}from"./chunks/chunk-EDLRMP2K.js";import{a as v,b as n,c as A,d as O,f as C}from"./chunks/chunk-QVNDDONZ.js";function S(i){return i.active??i.values[0]}function g(i,e){return i.disabled.has(e)||i.active===e?null:e}function K(i,e,o){let s=R({orientation:i.orientation,dir:i.dir,homeEnd:!0,activate:!0})[o],l=w({action:s,navigable:i.values,currentKey:e});if(!l)return null;let d=l.target,u=i.activationMode==="automatic"||s==="activate";return{focus:d,activate:u?g(i,d):null}}var p=class extends H{#t;constructor(e,o){super(e),this.#t=o}onTriggerClick(e){let{active:o}=this.#t.state(),s=g({disabled:this.disabled(),active:o},e.value);if(s!==null){let l=this.ordered().map(([,d])=>d.value);this.#t.activate(s,l.indexOf(s))}}onTriggerKeydown(e,o){let{active:s,orientation:l,dir:d,activationMode:u}=this.#t.state(),b=this.ordered(),a=b.map(([,r])=>r.value),t=K({values:a,disabled:this.disabled(),active:s,orientation:l,dir:d,activationMode:u},e.value,o.key);t&&(o.preventDefault(),t.activate!==null&&this.#t.activate(t.activate,a.indexOf(t.activate)),b[a.indexOf(t.focus)][0].focus())}};var D="primitiv-tabs",k="primitiv-tabs-list",x="primitiv-tabs-trigger",I="primitiv-tabs-content";function z(i){let e=i.getAttribute("value");return i.childElementCount===1&&e!==null?{tab:i.firstElementChild,value:e}:(A(i,`<${x}> needs a value and exactly one element child (a <button>, an <a href> or a router link) to decorate.`),null)}var m=class extends C{#t;#e=!1;controller=new p(this,this.#a());reconcileOn=["value","disabled","label","orientation"];get value(){return this.getAttribute("value")??void 0}set value(e){this.setAttribute("value",e)}#a(){return{state:()=>({active:this.value,orientation:this.#i(),dir:this.direction(),activationMode:this.#n()}),activate:(e,o)=>{this.announce("primitiv-tabs-change",{value:e,index:o})&&(this.value=e)}}}#n(){let e=this.getAttribute("activation-mode");return e==="manual"?e:"automatic"}#i(){return this.getAttribute("orientation")==="vertical"?"vertical":"horizontal"}reconcile(){let e=this.#i(),o=this.value,s=this.rootId();n(this,"data-orientation",e);for(let a of this.parts(k)){n(a,"role","tablist"),n(a,"aria-orientation",e),n(a,"data-orientation",e);let t=a.getAttribute("label");t!==null&&n(a,"aria-label",t)}let l=new Map;for(let a of this.parts(I)){let t=a.getAttribute("value");t!==null&&l.set(t,a)}let d=this.parts(x).flatMap(a=>{let t=z(a);return t?[{trigger:a,...t}]:[]}),u=S({values:d.map(a=>a.value),active:o});this.controller.setTriggers(d.map(({trigger:a,tab:t,value:r})=>({value:r,element:t,disabled:a.hasAttribute("disabled")})));for(let{trigger:a,tab:t,value:r}of d){let c=l.get(r),M=r===o,E=a.hasAttribute("disabled");t.id||(t.id=f(s,"trigger",r)),c&&!c.id&&(c.id=f(s,"panel",r)),n(t,"role","tab"),n(t,"aria-selected",String(M)),n(t,"aria-controls",c?c.id:null),n(t,"tabindex",r===u?"0":"-1"),n(t,"aria-disabled",E?"true":null),n(t,"data-disabled",E?"":null),n(t,"data-state",M?"active":"inactive"),n(t,"data-orientation",e),c&&n(c,"aria-labelledby",t.id)}for(let[a,t]of l){let r=a===o;n(t,"role","tabpanel"),n(t,"tabindex","0"),n(t,"hidden",r?null:""),n(t,"data-state",r?"active":"inactive"),n(t,"data-orientation",e)}let b=l.get(o);this.#e&&o!==this.#t&&b&&b.dispatchEvent(new CustomEvent("primitiv-tabs-show",{bubbles:!0})),this.#t=o,this.#e=!0}},h=class extends v{},T=class extends v{},y=class extends v{};function L(i=globalThis.customElements){O(i,[[D,m],[k,h],[x,T],[I,y]])}L();
@@ -516,6 +516,376 @@
516
516
  }
517
517
  ]
518
518
  },
519
+ {
520
+ "kind": "javascript-module",
521
+ "path": "src/modal/elements.ts",
522
+ "declarations": [
523
+ {
524
+ "kind": "class",
525
+ "description": "`<primitiv-modal>` — the state owner of a modal dialog, built on the native\n`<dialog>` and its `showModal()`: the browser makes the page behind it inert,\npaints the `::backdrop` and owns the top layer, and the element adds the\nrest of the WAI-ARIA Modal Dialog pattern — Tab wraps inside the dialog,\nEscape and a press on the backdrop close it, and closing hands focus back to\nthe button that opened it.\n\nThe root finds its own parts, so a modal nested in a dialog keeps its own,\nand reconciles their ARIA from its reflected state whenever a part arrives,\nleaves, or changes an attribute the root reads (the lifecycle is\n`PrimitivRootElement`'s). Leave `open` off the `<dialog>` itself: the root\nopens it, modally.",
526
+ "name": "PrimitivModalElement",
527
+ "members": [
528
+ {
529
+ "kind": "field",
530
+ "name": "#dialog",
531
+ "privacy": "private",
532
+ "type": {
533
+ "text": "HTMLDialogElement | null"
534
+ },
535
+ "default": "null",
536
+ "description": "The dialog the root currently decorates, and the controller over it."
537
+ },
538
+ {
539
+ "kind": "field",
540
+ "name": "#modal",
541
+ "privacy": "private",
542
+ "type": {
543
+ "text": "ModalController | null"
544
+ },
545
+ "default": "null"
546
+ },
547
+ {
548
+ "kind": "field",
549
+ "name": "#openers",
550
+ "privacy": "private",
551
+ "type": {
552
+ "text": "HTMLElement[]"
553
+ },
554
+ "default": "[]",
555
+ "description": "The decorated trigger and close buttons, as of the last reconcile."
556
+ },
557
+ {
558
+ "kind": "field",
559
+ "name": "#closers",
560
+ "privacy": "private",
561
+ "type": {
562
+ "text": "HTMLElement[]"
563
+ },
564
+ "default": "[]"
565
+ },
566
+ {
567
+ "kind": "field",
568
+ "name": "#listening",
569
+ "privacy": "private",
570
+ "type": {
571
+ "text": "AbortController | null"
572
+ },
573
+ "default": "null",
574
+ "description": "The root's own click listener, while connected."
575
+ },
576
+ {
577
+ "kind": "field",
578
+ "name": "controller",
579
+ "type": {
580
+ "text": "{ connect(): void; disconnect(): void }"
581
+ },
582
+ "privacy": "protected",
583
+ "readonly": true,
584
+ "default": "{ connect: (): void => this.#connect(), disconnect: (): void => this.#disconnect(), }"
585
+ },
586
+ {
587
+ "kind": "field",
588
+ "name": "reconcileOn",
589
+ "type": {
590
+ "text": "readonly string[]"
591
+ },
592
+ "privacy": "protected",
593
+ "readonly": true,
594
+ "default": "[\"open\"]"
595
+ },
596
+ {
597
+ "kind": "field",
598
+ "name": "open",
599
+ "type": {
600
+ "text": "boolean"
601
+ },
602
+ "description": "Whether the dialog is open, reflected as the boolean `open` attribute."
603
+ },
604
+ {
605
+ "kind": "method",
606
+ "name": "#host",
607
+ "privacy": "private",
608
+ "return": {
609
+ "type": {
610
+ "text": "ModalControllerHost"
611
+ }
612
+ },
613
+ "description": "The controller's view of this element: state lives in the reflected attribute."
614
+ },
615
+ {
616
+ "kind": "method",
617
+ "name": "#change",
618
+ "privacy": "private",
619
+ "return": {
620
+ "type": {
621
+ "text": "void"
622
+ }
623
+ },
624
+ "parameters": [
625
+ {
626
+ "name": "open",
627
+ "type": {
628
+ "text": "boolean"
629
+ }
630
+ }
631
+ ],
632
+ "description": "A change the user asked for: announced first, so a listener can veto it."
633
+ },
634
+ {
635
+ "kind": "method",
636
+ "name": "#connect",
637
+ "privacy": "private",
638
+ "return": {
639
+ "type": {
640
+ "text": "void"
641
+ }
642
+ }
643
+ },
644
+ {
645
+ "kind": "method",
646
+ "name": "#disconnect",
647
+ "privacy": "private",
648
+ "return": {
649
+ "type": {
650
+ "text": "void"
651
+ }
652
+ }
653
+ },
654
+ {
655
+ "kind": "method",
656
+ "name": "#click",
657
+ "privacy": "private",
658
+ "return": {
659
+ "type": {
660
+ "text": "void"
661
+ }
662
+ },
663
+ "parameters": [
664
+ {
665
+ "name": "event",
666
+ "type": {
667
+ "text": "MouseEvent"
668
+ }
669
+ }
670
+ ]
671
+ },
672
+ {
673
+ "kind": "method",
674
+ "name": "#use",
675
+ "privacy": "private",
676
+ "return": {
677
+ "type": {
678
+ "text": "void"
679
+ }
680
+ },
681
+ "parameters": [
682
+ {
683
+ "name": "dialog",
684
+ "type": {
685
+ "text": "HTMLDialogElement | null"
686
+ }
687
+ }
688
+ ],
689
+ "description": "Decorate `dialog` from now on: the controller follows it. Only a reconcile\ncalls this, and a root reconciles only while connected, so the new\ncontroller listens at once."
690
+ },
691
+ {
692
+ "kind": "method",
693
+ "name": "reconcile",
694
+ "privacy": "protected",
695
+ "return": {
696
+ "type": {
697
+ "text": "void"
698
+ }
699
+ }
700
+ },
701
+ {
702
+ "kind": "method",
703
+ "name": "#buttons",
704
+ "privacy": "private",
705
+ "return": {
706
+ "type": {
707
+ "text": "HTMLElement[]"
708
+ }
709
+ },
710
+ "parameters": [
711
+ {
712
+ "name": "selector",
713
+ "type": {
714
+ "text": "string"
715
+ }
716
+ }
717
+ ],
718
+ "description": "The buttons this root's `selector` parts decorate."
719
+ }
720
+ ],
721
+ "events": [
722
+ {
723
+ "type": {
724
+ "text": "CustomEvent<{ open: boolean }>"
725
+ },
726
+ "description": "Bubbles, cancelable, fired before a user-driven change is applied — a trigger or close click, Escape, a press on the backdrop, or the dialog closing natively — with the open state afterwards. Call `preventDefault()` to veto it, e.g. to keep a form with unsaved changes open.",
727
+ "name": "primitiv-modal-change"
728
+ }
729
+ ],
730
+ "attributes": [
731
+ {
732
+ "type": {
733
+ "text": "boolean"
734
+ },
735
+ "description": "Whether the dialog is open. Reflected: it changes as the user opens and dismisses the dialog, so a server re-render or a history snapshot sees the live state. Set it (or the `open` property) to open or close the dialog from script.",
736
+ "name": "open"
737
+ }
738
+ ],
739
+ "superclass": {
740
+ "name": "PrimitivRootElement",
741
+ "module": "/src/internal/RootElement.ts"
742
+ },
743
+ "tagName": "primitiv-modal",
744
+ "customElement": true,
745
+ "summary": "The state owner of a modal dialog on the native `<dialog>`."
746
+ },
747
+ {
748
+ "kind": "class",
749
+ "description": "`<primitiv-modal-trigger>` — an interactive part. It decorates exactly one\nchild the consumer writes, a `<button>`, which opens the dialog and receives\n`aria-haspopup=\"dialog\"`, `aria-expanded` and `aria-controls`, so the\nrendered DOM matches React's (RFC 0033 D24). A modal may have several.",
750
+ "name": "PrimitivModalTriggerElement",
751
+ "superclass": {
752
+ "name": "ElementBase",
753
+ "module": "/src/internal/element.ts"
754
+ },
755
+ "tagName": "primitiv-modal-trigger",
756
+ "customElement": true
757
+ },
758
+ {
759
+ "kind": "class",
760
+ "description": "`<primitiv-modal-content>` — decorates exactly one native `<dialog>` the\nconsumer writes, which the root opens with `showModal()`, closes, and gives\nan id, `data-state`, and `aria-labelledby` / `aria-describedby` pointing at\nthe title and description. The dialog keeps its implicit `dialog` role.",
761
+ "name": "PrimitivModalContentElement",
762
+ "superclass": {
763
+ "name": "ElementBase",
764
+ "module": "/src/internal/element.ts"
765
+ },
766
+ "tagName": "primitiv-modal-content",
767
+ "customElement": true
768
+ },
769
+ {
770
+ "kind": "class",
771
+ "description": "`<primitiv-modal-title>` — decorates exactly one heading the consumer writes\n(an `<h2>`, usually), whose id becomes the dialog's `aria-labelledby`, so it\nis the name a screen reader announces as the dialog opens.",
772
+ "name": "PrimitivModalTitleElement",
773
+ "superclass": {
774
+ "name": "ElementBase",
775
+ "module": "/src/internal/element.ts"
776
+ },
777
+ "tagName": "primitiv-modal-title",
778
+ "customElement": true
779
+ },
780
+ {
781
+ "kind": "class",
782
+ "description": "`<primitiv-modal-description>` — decorates exactly one element the consumer\nwrites (a `<p>`, usually), whose id becomes the dialog's `aria-describedby`.",
783
+ "name": "PrimitivModalDescriptionElement",
784
+ "superclass": {
785
+ "name": "ElementBase",
786
+ "module": "/src/internal/element.ts"
787
+ },
788
+ "tagName": "primitiv-modal-description",
789
+ "customElement": true
790
+ },
791
+ {
792
+ "kind": "class",
793
+ "description": "`<primitiv-modal-close>` — an interactive part. It decorates exactly one\n`<button>` the consumer writes, which closes the dialog. A modal may have\nseveral: the header's icon button and the footer's Cancel.",
794
+ "name": "PrimitivModalCloseElement",
795
+ "superclass": {
796
+ "name": "ElementBase",
797
+ "module": "/src/internal/element.ts"
798
+ },
799
+ "tagName": "primitiv-modal-close",
800
+ "customElement": true
801
+ },
802
+ {
803
+ "kind": "function",
804
+ "name": "defineModal",
805
+ "return": {
806
+ "type": {
807
+ "text": "void"
808
+ }
809
+ },
810
+ "parameters": [
811
+ {
812
+ "name": "registry",
813
+ "default": "globalThis.customElements",
814
+ "type": {
815
+ "text": "ElementRegistry | undefined"
816
+ }
817
+ }
818
+ ],
819
+ "description": "Define the six Modal elements on `registry` (the global `customElements` by\ndefault). Safe to call more than once, and a no-op where there is no\nregistry (a server render)."
820
+ }
821
+ ],
822
+ "exports": [
823
+ {
824
+ "kind": "js",
825
+ "name": "ElementRegistry",
826
+ "declaration": {
827
+ "name": "ElementRegistry",
828
+ "module": "src/modal/elements.ts"
829
+ }
830
+ },
831
+ {
832
+ "kind": "js",
833
+ "name": "PrimitivModalElement",
834
+ "declaration": {
835
+ "name": "PrimitivModalElement",
836
+ "module": "src/modal/elements.ts"
837
+ }
838
+ },
839
+ {
840
+ "kind": "js",
841
+ "name": "PrimitivModalTriggerElement",
842
+ "declaration": {
843
+ "name": "PrimitivModalTriggerElement",
844
+ "module": "src/modal/elements.ts"
845
+ }
846
+ },
847
+ {
848
+ "kind": "js",
849
+ "name": "PrimitivModalContentElement",
850
+ "declaration": {
851
+ "name": "PrimitivModalContentElement",
852
+ "module": "src/modal/elements.ts"
853
+ }
854
+ },
855
+ {
856
+ "kind": "js",
857
+ "name": "PrimitivModalTitleElement",
858
+ "declaration": {
859
+ "name": "PrimitivModalTitleElement",
860
+ "module": "src/modal/elements.ts"
861
+ }
862
+ },
863
+ {
864
+ "kind": "js",
865
+ "name": "PrimitivModalDescriptionElement",
866
+ "declaration": {
867
+ "name": "PrimitivModalDescriptionElement",
868
+ "module": "src/modal/elements.ts"
869
+ }
870
+ },
871
+ {
872
+ "kind": "js",
873
+ "name": "PrimitivModalCloseElement",
874
+ "declaration": {
875
+ "name": "PrimitivModalCloseElement",
876
+ "module": "src/modal/elements.ts"
877
+ }
878
+ },
879
+ {
880
+ "kind": "js",
881
+ "name": "defineModal",
882
+ "declaration": {
883
+ "name": "defineModal",
884
+ "module": "src/modal/elements.ts"
885
+ }
886
+ }
887
+ ]
888
+ },
519
889
  {
520
890
  "kind": "javascript-module",
521
891
  "path": "src/tabs/elements.ts",
@@ -4,4 +4,5 @@
4
4
  */
5
5
  import "./accordion.js";
6
6
  import "./collapsible.js";
7
+ import "./modal.js";
7
8
  import "./tabs.js";
@@ -4,4 +4,5 @@
4
4
  */
5
5
  import "./accordion.js";
6
6
  import "./collapsible.js";
7
+ import "./modal.js";
7
8
  import "./tabs.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Side-effect entry: importing this module defines the six Modal elements
3
+ * (`<primitiv-modal>`, `-trigger`, `-content`, `-title`, `-description`,
4
+ * `-close`) on the global `customElements` registry.
5
+ *
6
+ * ```html
7
+ * <script type="module">import "@primitiv-ui/web/define/modal";</script>
8
+ * ```
9
+ */
10
+ import { defineModal } from "../modal/elements.js";
11
+ defineModal();
@@ -15,3 +15,7 @@ export type PrimitivAccordionChangeEvent = CustomEvent<{
15
15
  export type PrimitivCollapsibleChangeEvent = CustomEvent<{
16
16
  open: boolean;
17
17
  }>;
18
+ /** The `primitiv-modal-change` event: bubbles, cancelable, fired before the change applies, with the open state afterwards. */
19
+ export type PrimitivModalChangeEvent = CustomEvent<{
20
+ open: boolean;
21
+ }>;
@@ -20,13 +20,14 @@
20
20
  import type { DetailedHTMLProps, HTMLAttributes } from "react";
21
21
  import type { PrimitivAccordionContentElement, PrimitivAccordionElement, PrimitivAccordionItemElement, PrimitivAccordionTriggerElement, PrimitivAccordionTriggerIconElement } from "../accordion/elements.js";
22
22
  import type { PrimitivCollapsibleContentElement, PrimitivCollapsibleElement, PrimitivCollapsibleTriggerElement, PrimitivCollapsibleTriggerIconElement } from "../collapsible/elements.js";
23
+ import type { PrimitivModalCloseElement, PrimitivModalContentElement, PrimitivModalDescriptionElement, PrimitivModalElement, PrimitivModalTitleElement, PrimitivModalTriggerElement } from "../modal/elements.js";
23
24
  import type { PrimitivTabsContentElement, PrimitivTabsElement, PrimitivTabsListElement, PrimitivTabsTriggerElement } from "../tabs/elements.js";
24
- import type { PrimitivAccordionChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivTabsChangeEvent } from "./events.js";
25
+ import type { PrimitivAccordionChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivModalChangeEvent, PrimitivTabsChangeEvent } from "./events.js";
25
26
  /** React's own props for an element, plus `class`, which React 19 passes to custom elements. */
26
27
  type ElementProps<E extends HTMLElement> = DetailedHTMLProps<HTMLAttributes<E>, E> & {
27
28
  class?: string;
28
29
  };
29
- export type { PrimitivAccordionChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivTabsChangeEvent };
30
+ export type { PrimitivAccordionChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivModalChangeEvent, PrimitivTabsChangeEvent, };
30
31
  declare module "react" {
31
32
  namespace JSX {
32
33
  interface IntrinsicElements {
@@ -59,6 +60,15 @@ declare module "react" {
59
60
  "onprimitiv-collapsible-show"?: (event: CustomEvent) => void;
60
61
  };
61
62
  "primitiv-collapsible-trigger-icon": ElementProps<PrimitivCollapsibleTriggerIconElement>;
63
+ "primitiv-modal": ElementProps<PrimitivModalElement> & {
64
+ open?: boolean;
65
+ "onprimitiv-modal-change"?: (event: PrimitivModalChangeEvent) => void;
66
+ };
67
+ "primitiv-modal-trigger": ElementProps<PrimitivModalTriggerElement>;
68
+ "primitiv-modal-content": ElementProps<PrimitivModalContentElement>;
69
+ "primitiv-modal-title": ElementProps<PrimitivModalTitleElement>;
70
+ "primitiv-modal-description": ElementProps<PrimitivModalDescriptionElement>;
71
+ "primitiv-modal-close": ElementProps<PrimitivModalCloseElement>;
62
72
  "primitiv-tabs": ElementProps<PrimitivTabsElement> & {
63
73
  value?: string;
64
74
  orientation?: "horizontal" | "vertical";