@data-slot/disclosure 0.1.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/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @data-slot/disclosure
2
+
3
+ Headless disclosure (show/hide) component for vanilla JavaScript. Accessible, unstyled, tiny.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @data-slot/disclosure
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```html
14
+ <div data-slot="disclosure">
15
+ <button data-slot="disclosure-trigger">Toggle Content</button>
16
+ <div data-slot="disclosure-content" hidden>
17
+ Hidden content revealed on click.
18
+ </div>
19
+ </div>
20
+
21
+ <script type="module">
22
+ import { create } from "@data-slot/disclosure";
23
+
24
+ const controllers = create();
25
+ </script>
26
+ ```
27
+
28
+ ## API
29
+
30
+ ### `create(scope?)`
31
+
32
+ Auto-discover and bind all disclosure instances in a scope (defaults to `document`).
33
+
34
+ ```typescript
35
+ import { create } from "@data-slot/disclosure";
36
+
37
+ const controllers = create(); // Returns DisclosureController[]
38
+ ```
39
+
40
+ ### `createDisclosure(root, options?)`
41
+
42
+ Create a controller for a specific element.
43
+
44
+ ```typescript
45
+ import { createDisclosure } from "@data-slot/disclosure";
46
+
47
+ const disclosure = createDisclosure(element, {
48
+ defaultOpen: false,
49
+ onOpenChange: (open) => console.log(open),
50
+ });
51
+ ```
52
+
53
+ ### Options
54
+
55
+ | Option | Type | Default | Description |
56
+ |--------|------|---------|-------------|
57
+ | `defaultOpen` | `boolean` | `false` | Initial open state |
58
+ | `onOpenChange` | `(open: boolean) => void` | `undefined` | Callback when open state changes |
59
+
60
+ ### Controller
61
+
62
+ | Method/Property | Description |
63
+ |-----------------|-------------|
64
+ | `open()` | Open the disclosure |
65
+ | `close()` | Close the disclosure |
66
+ | `toggle()` | Toggle the disclosure |
67
+ | `isOpen` | Current open state (readonly `boolean`) |
68
+ | `destroy()` | Cleanup all event listeners |
69
+
70
+ ## Markup Structure
71
+
72
+ ```html
73
+ <div data-slot="disclosure">
74
+ <button data-slot="disclosure-trigger">Toggle</button>
75
+ <div data-slot="disclosure-content">Content</div>
76
+ </div>
77
+ ```
78
+
79
+ Both `disclosure-trigger` and `disclosure-content` are required.
80
+
81
+ ## Styling
82
+
83
+ Use `data-state` attributes for CSS styling:
84
+
85
+ ```css
86
+ /* Hidden state */
87
+ [data-slot="disclosure-content"][hidden] {
88
+ display: none;
89
+ }
90
+
91
+ /* Or use data-state */
92
+ [data-slot="disclosure"][data-state="closed"] [data-slot="disclosure-content"] {
93
+ display: none;
94
+ }
95
+
96
+ /* Animate */
97
+ [data-slot="disclosure-content"] {
98
+ overflow: hidden;
99
+ transition: max-height 0.3s;
100
+ max-height: 0;
101
+ }
102
+
103
+ [data-slot="disclosure"][data-state="open"] [data-slot="disclosure-content"] {
104
+ max-height: 500px;
105
+ }
106
+ ```
107
+
108
+ With Tailwind:
109
+
110
+ ```html
111
+ <div data-slot="disclosure" class="group">
112
+ <button data-slot="disclosure-trigger" class="flex items-center gap-2">
113
+ <span>Show more</span>
114
+ <svg class="group-data-[state=open]:rotate-180 transition-transform">...</svg>
115
+ </button>
116
+ <div data-slot="disclosure-content" class="hidden group-data-[state=open]:block">
117
+ Content here
118
+ </div>
119
+ </div>
120
+ ```
121
+
122
+ ## Accessibility
123
+
124
+ The component automatically handles:
125
+
126
+ - `aria-expanded` state on trigger
127
+ - `aria-controls` linking trigger to content
128
+ - Unique ID generation for content
129
+
130
+ ## Events
131
+
132
+ Listen for changes via custom events:
133
+
134
+ ```javascript
135
+ element.addEventListener("disclosure:change", (e) => {
136
+ console.log("Disclosure open:", e.detail.open);
137
+ });
138
+ ```
139
+
140
+ ## License
141
+
142
+ MIT
143
+
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=s(require(`@data-slot/core`));function l(e,t={}){let{defaultOpen:n=!1,onOpenChange:r}=t,i=(0,c.getPart)(e,`disclosure-trigger`),a=(0,c.getPart)(e,`disclosure-content`);if(!i||!a)throw Error(`Disclosure requires trigger and content slots`);let o=n,s=[],l=(0,c.ensureId)(a,`disclosure-content`);i.setAttribute(`aria-controls`,l);let u=t=>{o!==t&&(o=t,(0,c.setAria)(i,`expanded`,o),a.hidden=!o,e.setAttribute(`data-state`,o?`open`:`closed`),(0,c.emit)(e,`disclosure:change`,{open:o}),r?.(o))};(0,c.setAria)(i,`expanded`,o),a.hidden=!o,e.setAttribute(`data-state`,o?`open`:`closed`),s.push((0,c.on)(i,`click`,()=>u(!o)));let d={open:()=>u(!0),close:()=>u(!1),toggle:()=>u(!o),get isOpen(){return o},destroy:()=>{s.forEach(e=>e()),s.length=0}};return d}const u=new WeakSet;function d(e=document){let t=[];for(let n of(0,c.getRoots)(e,`disclosure`)){if(u.has(n))continue;u.add(n),t.push(l(n))}return t}exports.create=d,exports.createDisclosure=l;
@@ -0,0 +1,38 @@
1
+ //#region src/index.d.ts
2
+ interface DisclosureOptions {
3
+ /** Initial open state */
4
+ defaultOpen?: boolean;
5
+ /** Callback when open state changes */
6
+ onOpenChange?: (open: boolean) => void;
7
+ }
8
+ interface DisclosureController {
9
+ /** Open the disclosure */
10
+ open(): void;
11
+ /** Close the disclosure */
12
+ close(): void;
13
+ /** Toggle the disclosure */
14
+ toggle(): void;
15
+ /** Current open state */
16
+ readonly isOpen: boolean;
17
+ /** Cleanup all event listeners */
18
+ destroy(): void;
19
+ }
20
+ /**
21
+ * Create a disclosure controller for a root element
22
+ *
23
+ * Expected markup:
24
+ * ```html
25
+ * <div data-slot="disclosure">
26
+ * <button data-slot="disclosure-trigger">Toggle</button>
27
+ * <div data-slot="disclosure-content">Content here</div>
28
+ * </div>
29
+ * ```
30
+ */
31
+ declare function createDisclosure(root: Element, options?: DisclosureOptions): DisclosureController;
32
+ /**
33
+ * Find and bind all disclosure components in a scope
34
+ * Returns array of controllers for programmatic access
35
+ */
36
+ declare function create(scope?: ParentNode): DisclosureController[];
37
+ //#endregion
38
+ export { DisclosureController, DisclosureOptions, create, createDisclosure };
@@ -0,0 +1,38 @@
1
+ //#region src/index.d.ts
2
+ interface DisclosureOptions {
3
+ /** Initial open state */
4
+ defaultOpen?: boolean;
5
+ /** Callback when open state changes */
6
+ onOpenChange?: (open: boolean) => void;
7
+ }
8
+ interface DisclosureController {
9
+ /** Open the disclosure */
10
+ open(): void;
11
+ /** Close the disclosure */
12
+ close(): void;
13
+ /** Toggle the disclosure */
14
+ toggle(): void;
15
+ /** Current open state */
16
+ readonly isOpen: boolean;
17
+ /** Cleanup all event listeners */
18
+ destroy(): void;
19
+ }
20
+ /**
21
+ * Create a disclosure controller for a root element
22
+ *
23
+ * Expected markup:
24
+ * ```html
25
+ * <div data-slot="disclosure">
26
+ * <button data-slot="disclosure-trigger">Toggle</button>
27
+ * <div data-slot="disclosure-content">Content here</div>
28
+ * </div>
29
+ * ```
30
+ */
31
+ declare function createDisclosure(root: Element, options?: DisclosureOptions): DisclosureController;
32
+ /**
33
+ * Find and bind all disclosure components in a scope
34
+ * Returns array of controllers for programmatic access
35
+ */
36
+ declare function create(scope?: ParentNode): DisclosureController[];
37
+ //#endregion
38
+ export { DisclosureController, DisclosureOptions, create, createDisclosure };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{emit as e,ensureId as t,getPart as n,getRoots as r,on as i,setAria as a}from"@data-slot/core";function o(r,o={}){let{defaultOpen:s=!1,onOpenChange:c}=o,l=n(r,`disclosure-trigger`),u=n(r,`disclosure-content`);if(!l||!u)throw Error(`Disclosure requires trigger and content slots`);let d=s,f=[],p=t(u,`disclosure-content`);l.setAttribute(`aria-controls`,p);let m=t=>{d!==t&&(d=t,a(l,`expanded`,d),u.hidden=!d,r.setAttribute(`data-state`,d?`open`:`closed`),e(r,`disclosure:change`,{open:d}),c?.(d))};a(l,`expanded`,d),u.hidden=!d,r.setAttribute(`data-state`,d?`open`:`closed`),f.push(i(l,`click`,()=>m(!d)));let h={open:()=>m(!0),close:()=>m(!1),toggle:()=>m(!d),get isOpen(){return d},destroy:()=>{f.forEach(e=>e()),f.length=0}};return h}const s=new WeakSet;function c(e=document){let t=[];for(let n of r(e,`disclosure`)){if(s.has(n))continue;s.add(n),t.push(o(n))}return t}export{c as create,o as createDisclosure};
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@data-slot/disclosure",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": ["dist"],
22
+ "scripts": {
23
+ "build": "tsdown"
24
+ },
25
+ "dependencies": {
26
+ "@data-slot/core": "workspace:*"
27
+ },
28
+ "keywords": ["headless", "ui", "disclosure", "vanilla", "data-slot"],
29
+ "license": "MIT"
30
+ }