@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 +143 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +38 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +1 -0
- package/package.json +30 -0
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;
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|