@fpkit/acss 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/libs/chunk-25KCUE3R.cjs +17 -0
  2. package/libs/chunk-25KCUE3R.cjs.map +1 -0
  3. package/libs/chunk-34NWHFHP.js +10 -0
  4. package/libs/chunk-34NWHFHP.js.map +1 -0
  5. package/libs/{chunk-SQ44OCJ2.js → chunk-6NMLU5FA.js} +2 -2
  6. package/libs/{chunk-GVVCXXKI.cjs → chunk-6YVR4TDM.cjs} +3 -3
  7. package/libs/chunk-DSQ2TUCR.js +7 -0
  8. package/libs/chunk-DSQ2TUCR.js.map +1 -0
  9. package/libs/{chunk-H6A2CUWA.js → chunk-VQTCTLFN.js} +2 -2
  10. package/libs/chunk-ZJ4RUKI2.cjs +14 -0
  11. package/libs/chunk-ZJ4RUKI2.cjs.map +1 -0
  12. package/libs/{chunk-H4JRUNKU.cjs → chunk-ZOPHCNFD.cjs} +3 -3
  13. package/libs/components/button.cjs +3 -3
  14. package/libs/components/button.d.cts +34 -1
  15. package/libs/components/button.d.ts +34 -1
  16. package/libs/components/button.js +1 -1
  17. package/libs/components/buttons/button.css +1 -1
  18. package/libs/components/buttons/button.css.map +1 -1
  19. package/libs/components/buttons/button.min.css +2 -2
  20. package/libs/components/buttons/icon-button.css +1 -0
  21. package/libs/components/buttons/icon-button.css.map +1 -0
  22. package/libs/components/buttons/icon-button.min.css +3 -0
  23. package/libs/components/dialog/dialog.cjs +4 -4
  24. package/libs/components/dialog/dialog.js +2 -2
  25. package/libs/components/icons/icon.d.cts +1 -1
  26. package/libs/components/icons/icon.d.ts +1 -1
  27. package/libs/components/link/link.css +1 -1
  28. package/libs/components/link/link.min.css +1 -1
  29. package/libs/components/modal.cjs +3 -3
  30. package/libs/components/modal.js +2 -2
  31. package/libs/components/popover/popover.cjs +3 -8
  32. package/libs/components/popover/popover.css +1 -0
  33. package/libs/components/popover/popover.css.map +1 -0
  34. package/libs/components/popover/popover.d.cts +54 -26
  35. package/libs/components/popover/popover.d.ts +54 -26
  36. package/libs/components/popover/popover.js +1 -2
  37. package/libs/components/popover/popover.min.css +3 -0
  38. package/libs/hooks.cjs +3 -6
  39. package/libs/hooks.cjs.map +1 -1
  40. package/libs/hooks.d.cts +30 -10
  41. package/libs/hooks.d.ts +30 -10
  42. package/libs/hooks.js +5 -1
  43. package/libs/hooks.js.map +1 -1
  44. package/libs/{icons-48788561.d.ts → icons-2c09535c.d.ts} +32 -32
  45. package/libs/icons.d.cts +1 -1
  46. package/libs/icons.d.ts +1 -1
  47. package/libs/index.cjs +35 -35
  48. package/libs/index.cjs.map +1 -1
  49. package/libs/index.css +1 -1
  50. package/libs/index.css.map +1 -1
  51. package/libs/index.d.cts +64 -5
  52. package/libs/index.d.ts +64 -5
  53. package/libs/index.js +9 -10
  54. package/libs/index.js.map +1 -1
  55. package/package.json +2 -2
  56. package/src/components/buttons/README.mdx +107 -11
  57. package/src/components/buttons/STYLES.mdx +182 -47
  58. package/src/components/buttons/button.scss +93 -16
  59. package/src/components/buttons/button.stories.tsx +149 -0
  60. package/src/components/buttons/button.test.tsx +12 -0
  61. package/src/components/buttons/button.tsx +50 -6
  62. package/src/components/buttons/icon-button.scss +45 -0
  63. package/src/components/buttons/icon-button.stories.tsx +200 -0
  64. package/src/components/buttons/icon-button.test.tsx +132 -0
  65. package/src/components/buttons/icon-button.tsx +72 -0
  66. package/src/components/form/select.tsx +55 -51
  67. package/src/components/link/link.scss +2 -2
  68. package/src/components/popover/README.mdx +478 -0
  69. package/src/components/popover/STYLES.mdx +389 -0
  70. package/src/components/popover/index.ts +3 -0
  71. package/src/components/popover/popover.scss +249 -0
  72. package/src/components/popover/popover.stories.tsx +315 -15
  73. package/src/components/popover/popover.test.tsx +249 -37
  74. package/src/components/popover/popover.tsx +165 -62
  75. package/src/hooks/popover/popover.tsx +26 -10
  76. package/src/hooks/popover/use-popover.tsx +30 -10
  77. package/src/hooks.ts +5 -0
  78. package/src/index.scss +1 -0
  79. package/src/index.ts +1 -0
  80. package/src/styles/buttons/button.css +78 -16
  81. package/src/styles/buttons/button.css.map +1 -1
  82. package/src/styles/buttons/icon-button.css +32 -0
  83. package/src/styles/buttons/icon-button.css.map +1 -0
  84. package/src/styles/index.css +268 -18
  85. package/src/styles/index.css.map +1 -1
  86. package/src/styles/link/link.css +2 -2
  87. package/src/styles/popover/popover.css +190 -0
  88. package/src/styles/popover/popover.css.map +1 -0
  89. package/src/types/popover.d.ts +64 -0
  90. package/libs/chunk-4I5MF54P.js +0 -8
  91. package/libs/chunk-4I5MF54P.js.map +0 -1
  92. package/libs/chunk-GCGKYLDG.js +0 -7
  93. package/libs/chunk-GCGKYLDG.js.map +0 -1
  94. package/libs/chunk-NZVSXRTB.cjs +0 -16
  95. package/libs/chunk-NZVSXRTB.cjs.map +0 -1
  96. package/libs/chunk-PDD4N5P5.cjs +0 -10
  97. package/libs/chunk-PDD4N5P5.cjs.map +0 -1
  98. package/libs/chunk-S7NIA6PI.cjs +0 -17
  99. package/libs/chunk-S7NIA6PI.cjs.map +0 -1
  100. package/libs/chunk-X2RDXWH5.js +0 -10
  101. package/libs/chunk-X2RDXWH5.js.map +0 -1
  102. /package/libs/{chunk-SQ44OCJ2.js.map → chunk-6NMLU5FA.js.map} +0 -0
  103. /package/libs/{chunk-GVVCXXKI.cjs.map → chunk-6YVR4TDM.cjs.map} +0 -0
  104. /package/libs/{chunk-H6A2CUWA.js.map → chunk-VQTCTLFN.js.map} +0 -0
  105. /package/libs/{chunk-H4JRUNKU.cjs.map → chunk-ZOPHCNFD.cjs.map} +0 -0
@@ -0,0 +1,478 @@
1
+ import { Meta } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="FP.React Components/Popover/Readme" />
4
+
5
+ # Popover
6
+
7
+ The Popover component uses the native HTML Popover API to display floating content relative to a trigger element. It provides automatic top-layer rendering, light dismiss behavior, and built-in accessibility features.
8
+
9
+ ## Features
10
+
11
+ - **Native API**: Uses HTML `popover` attribute for automatic layer management
12
+ - **Dismiss Modes**: Auto (light dismiss) or manual (explicit close required)
13
+ - **Positioning**: CSS anchor positioning with placement hints
14
+ - **Accessibility**: Built-in focus management, Escape key handling, and ARIA support
15
+ - **Customizable**: CSS custom properties for complete theming control
16
+ - **TypeScript**: Full type safety with comprehensive prop types
17
+
18
+ ## Browser Requirements
19
+
20
+ - Chrome 125+
21
+ - Edge 125+
22
+ - Safari 17.4+
23
+
24
+ Requires native Popover API and CSS anchor positioning support.
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install @fpkit/acss
30
+ ```
31
+
32
+ ## Basic Usage
33
+
34
+ ```tsx
35
+ import { Popover } from '@fpkit/acss';
36
+ import '@fpkit/acss/styles';
37
+
38
+ function App() {
39
+ return (
40
+ <Popover id="my-popover" triggerLabel="Open Menu">
41
+ <h3>Menu</h3>
42
+ <p>Popover content here</p>
43
+ </Popover>
44
+ );
45
+ }
46
+ ```
47
+
48
+ ## Examples
49
+
50
+ ### Default (Auto Mode)
51
+
52
+ ```tsx
53
+ <Popover id="default-popover" triggerLabel="Open Popover">
54
+ <h3>Popover Title</h3>
55
+ <p>
56
+ This popover dismisses automatically when you click outside or press
57
+ Escape.
58
+ </p>
59
+ </Popover>
60
+ ```
61
+
62
+ ### Manual Mode
63
+
64
+ Requires explicit close action and shows a backdrop overlay.
65
+
66
+ ```tsx
67
+ <Popover
68
+ id="manual-popover"
69
+ triggerLabel="Open Manual Popover"
70
+ mode="manual"
71
+ >
72
+ <h3>Manual Popover</h3>
73
+ <p>
74
+ This popover requires clicking the close button or trigger to dismiss.
75
+ It includes a backdrop overlay.
76
+ </p>
77
+ </Popover>
78
+ ```
79
+
80
+ ### Placement Options
81
+
82
+ ```tsx
83
+ {/* Top placement */}
84
+ <Popover id="top-popover" triggerLabel="Open Above" placement="top">
85
+ <p>This popover appears above the trigger</p>
86
+ </Popover>
87
+
88
+ {/* Left placement */}
89
+ <Popover id="left-popover" triggerLabel="Open Left" placement="left">
90
+ <p>This popover appears to the left</p>
91
+ </Popover>
92
+
93
+ {/* Right placement */}
94
+ <Popover id="right-popover" triggerLabel="Open Right" placement="right">
95
+ <p>This popover appears to the right</p>
96
+ </Popover>
97
+ ```
98
+
99
+ ### Custom Trigger
100
+
101
+ Use any React element as the trigger.
102
+
103
+ ```tsx
104
+ <Popover
105
+ id="custom-trigger-popover"
106
+ trigger={
107
+ <button style={{ padding: "0.5rem 1rem", borderRadius: "2rem" }}>
108
+ 🎨 Custom
109
+ </button>
110
+ }
111
+ >
112
+ <h4>Custom Trigger</h4>
113
+ <p>You can use any React element as trigger</p>
114
+ </Popover>
115
+ ```
116
+
117
+ ### Custom Styling
118
+
119
+ Theme the popover using CSS custom properties.
120
+
121
+ ```tsx
122
+ <Popover
123
+ id="custom-styled-popover"
124
+ triggerLabel="Custom Style"
125
+ styles={{
126
+ '--popover-bg': '#1a1a2e',
127
+ '--popover-border': '0.125rem solid #16213e',
128
+ '--popover-border-radius': '0.75rem',
129
+ '--popover-padding': '1.5rem',
130
+ '--popover-shadow': '0 0.5rem 1rem rgba(0, 0, 0, 0.3)',
131
+ color: '#eee',
132
+ }}
133
+ >
134
+ <h3 style={{ color: '#0f3' }}>Dark Theme</h3>
135
+ <p>Customize appearance using CSS custom properties</p>
136
+ </Popover>
137
+ ```
138
+
139
+ ### Controlled State
140
+
141
+ Manage popover state externally.
142
+
143
+ ```tsx
144
+ import { useState } from 'react';
145
+
146
+ function ControlledExample() {
147
+ const [isOpen, setIsOpen] = useState(false);
148
+
149
+ return (
150
+ <>
151
+ <Popover
152
+ id="controlled-popover"
153
+ triggerLabel="Toggle Popover"
154
+ isOpen={isOpen}
155
+ onToggle={setIsOpen}
156
+ >
157
+ <h4>Controlled Popover</h4>
158
+ <p>State is managed externally</p>
159
+ <button onClick={() => setIsOpen(false)}>
160
+ Close via State
161
+ </button>
162
+ </Popover>
163
+
164
+ <div>
165
+ <p>Current state: {isOpen ? "Open" : "Closed"}</p>
166
+ <button onClick={() => setIsOpen(!isOpen)}>
167
+ External Toggle
168
+ </button>
169
+ </div>
170
+ </>
171
+ );
172
+ }
173
+ ```
174
+
175
+ ### With Form Content
176
+
177
+ ```tsx
178
+ <Popover
179
+ id="form-popover"
180
+ triggerLabel="Show Form"
181
+ mode="manual"
182
+ >
183
+ <form
184
+ onSubmit={(e) => {
185
+ e.preventDefault();
186
+ alert('Form submitted!');
187
+ }}
188
+ >
189
+ <h4>Contact Form</h4>
190
+ <input type="text" placeholder="Name" />
191
+ <input type="email" placeholder="Email" />
192
+ <textarea placeholder="Message" rows={3} />
193
+ <button type="submit">Submit</button>
194
+ </form>
195
+ </Popover>
196
+ ```
197
+
198
+ ## API Reference
199
+
200
+ ### Props
201
+
202
+ | Prop | Type | Default | Description |
203
+ |------|------|---------|-------------|
204
+ | `id` | `string` | auto-generated | Unique identifier for popover |
205
+ | `children` | `ReactNode` | required | Content to display in popover |
206
+ | `trigger` | `ReactNode` | - | Custom trigger element |
207
+ | `triggerLabel` | `string` | `"Open"` | Aria-label for default trigger |
208
+ | `mode` | `"auto" \| "manual"` | `"auto"` | Dismiss behavior |
209
+ | `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Preferred position |
210
+ | `isOpen` | `boolean` | - | Controlled open state |
211
+ | `onToggle` | `(open: boolean) => void` | - | Toggle callback |
212
+ | `showCloseButton` | `boolean` | `true` for manual | Show close button |
213
+ | `closeButtonLabel` | `string` | `"Close"` | Aria-label for close button |
214
+ | `showArrow` | `boolean` | `true` | Show positioning arrow |
215
+ | `className` | `string` | - | Custom CSS class |
216
+ | `styles` | `CSSProperties` | - | Inline CSS variables |
217
+
218
+ ### Modes
219
+
220
+ **Auto Mode** (default)
221
+ - Light dismiss: closes on Escape or outside click
222
+ - No backdrop overlay
223
+ - Close button hidden by default
224
+
225
+ **Manual Mode**
226
+ - Requires explicit close action
227
+ - Shows backdrop overlay
228
+ - Close button shown by default
229
+
230
+ ## Styling
231
+
232
+ The Popover component uses CSS custom properties for theming. See the **Styling** page in Storybook for complete styling documentation.
233
+
234
+ ### Quick Theme Example
235
+
236
+ ```tsx
237
+ <Popover
238
+ id="themed-popover"
239
+ styles={{
240
+ '--popover-bg': '#1a1a2e',
241
+ '--popover-border': '2px solid #16213e',
242
+ '--popover-border-radius': '12px',
243
+ '--popover-padding': '24px',
244
+ }}
245
+ >
246
+ <p>Themed popover</p>
247
+ </Popover>
248
+ ```
249
+
250
+ ## Accessibility
251
+
252
+ - **Keyboard Navigation**: Escape key closes auto-mode popovers
253
+ - **Focus Management**: Automatically managed by browser
254
+ - **ARIA Labels**: Proper labeling for triggers and close buttons
255
+ - **Screen Readers**: Semantic HTML with proper attributes
256
+
257
+ ## Best Practices
258
+
259
+ 1. **Always provide an ID**: While auto-generated IDs work, explicit IDs are more predictable
260
+ 2. **Use meaningful trigger labels**: Ensure `triggerLabel` or custom trigger has clear purpose
261
+ 3. **Choose appropriate mode**: Use auto for menus/tooltips, manual for forms/important content
262
+ 4. **Test across browsers**: Verify popover support in target browsers
263
+ 5. **Consider mobile**: Test touch interactions and viewport positioning
264
+
265
+ ## Controlled vs Uncontrolled
266
+
267
+ ### Uncontrolled (Default)
268
+
269
+ Let the browser manage state automatically:
270
+
271
+ ```tsx
272
+ <Popover id="uncontrolled" triggerLabel="Open">
273
+ <p>Content</p>
274
+ </Popover>
275
+ ```
276
+
277
+ ### Controlled
278
+
279
+ Manage state externally for complex scenarios:
280
+
281
+ ```tsx
282
+ const [isOpen, setIsOpen] = useState(false);
283
+
284
+ <Popover
285
+ id="controlled"
286
+ isOpen={isOpen}
287
+ onToggle={setIsOpen}
288
+ triggerLabel="Open"
289
+ >
290
+ <p>Content</p>
291
+ </Popover>
292
+ ```
293
+
294
+ ## Related Components
295
+
296
+ - **Dialog**: For modal overlays requiring user response
297
+ - **Tooltip**: For brief contextual information (consider Popover for richer content)
298
+ - **Dropdown**: For selection lists (can be built with Popover)
299
+
300
+ ## Migration from Old Popover
301
+
302
+ If upgrading from the previous hook-based Popover implementation (`usePopover` or the old `Popover` component from `@fpkit/acss/hooks`), follow this guide:
303
+
304
+ ### Breaking Changes
305
+
306
+ The legacy Popover has been replaced with a new implementation using the native HTML Popover API. The old implementation is **deprecated** and will be removed in **v3.0.0**.
307
+
308
+ ### Component Migration
309
+
310
+ **Before (Legacy - Deprecated):**
311
+ ```tsx
312
+ // Old hook-based approach
313
+ import { Popover } from '@fpkit/acss/hooks'; // ❌ Deprecated
314
+ // or
315
+ import { usePopover } from '@fpkit/acss/hooks'; // ❌ Deprecated
316
+
317
+ <Popover popoverTrigger={<button>Hover me</button>}>
318
+ <p>Hover-based popover content</p>
319
+ </Popover>
320
+ ```
321
+
322
+ **After (New - Recommended):**
323
+ ```tsx
324
+ // New native Popover API approach
325
+ import { Popover } from '@fpkit/acss'; // ✅ Recommended
326
+
327
+ <Popover
328
+ id="my-popover"
329
+ trigger={<button>Click me</button>}
330
+ >
331
+ <p>Click-based popover content</p>
332
+ </Popover>
333
+ ```
334
+
335
+ ### Hook Migration
336
+
337
+ If you were using the `usePopover` hook directly for custom implementations:
338
+
339
+ **Before (Legacy - Deprecated):**
340
+ ```tsx
341
+ import { usePopover } from '@fpkit/acss/hooks'; // ❌ Deprecated
342
+
343
+ function CustomPopover() {
344
+ const hoverRef = useRef(null);
345
+ const popoverRef = useRef(null);
346
+ const { isVisible, popoverPosition, handlePointerEvent, handlePointerLeave } =
347
+ usePopover(hoverRef, popoverRef, 10);
348
+
349
+ return (
350
+ <div>
351
+ <div
352
+ ref={hoverRef}
353
+ onPointerEnter={handlePointerEvent}
354
+ onPointerLeave={handlePointerLeave}
355
+ >
356
+ Hover trigger
357
+ </div>
358
+ {isVisible && (
359
+ <div
360
+ ref={popoverRef}
361
+ style={{
362
+ position: 'absolute',
363
+ top: popoverPosition.top,
364
+ left: popoverPosition.left,
365
+ }}
366
+ >
367
+ Popover content
368
+ </div>
369
+ )}
370
+ </div>
371
+ );
372
+ }
373
+ ```
374
+
375
+ **After (New - Recommended):**
376
+ ```tsx
377
+ import { Popover } from '@fpkit/acss'; // ✅ Recommended
378
+
379
+ function CustomPopover() {
380
+ return (
381
+ <Popover
382
+ id="custom-popover"
383
+ trigger={<button>Click trigger</button>}
384
+ placement="bottom"
385
+ >
386
+ <p>Popover content</p>
387
+ </Popover>
388
+ );
389
+ }
390
+ ```
391
+
392
+ ### Key Behavioral Changes
393
+
394
+ | Feature | Old (Legacy) | New (Native API) |
395
+ |---------|-------------|-----------------|
396
+ | **Trigger Method** | Hover/pointer events | Click (default native behavior) |
397
+ | **Positioning** | Manual calculation | Native CSS anchor positioning |
398
+ | **Layer Management** | z-index stacking | Automatic top-layer rendering |
399
+ | **Dismiss Behavior** | Manual pointer leave | Native light dismiss (Escape, outside click) |
400
+ | **Focus Management** | Manual implementation | Automatic browser handling |
401
+ | **Accessibility** | Limited ARIA support | Full native accessibility |
402
+ | **Browser Support** | All modern browsers | Chrome 125+, Safari 17.4+, Edge 125+ |
403
+
404
+ ### Prop Mapping
405
+
406
+ | Old Prop | New Prop | Notes |
407
+ |----------|----------|-------|
408
+ | `popoverTrigger` | `trigger` | Custom trigger element |
409
+ | `content` | `children` | Popover content |
410
+ | N/A | `id` | Required for native API (auto-generated if omitted) |
411
+ | N/A | `mode` | `"auto"` or `"manual"` dismiss behavior |
412
+ | N/A | `placement` | `"top"`, `"bottom"`, `"left"`, `"right"` |
413
+ | N/A | `isOpen` | Controlled state support |
414
+ | N/A | `onToggle` | Callback when popover opens/closes |
415
+ | N/A | `showArrow` | Show positioning arrow indicator |
416
+ | N/A | `styles` | CSS custom properties for theming |
417
+
418
+ ### Benefits of Migration
419
+
420
+ ✅ **Better Accessibility**: Native focus management and keyboard navigation
421
+ ✅ **Simpler API**: No manual positioning calculations required
422
+ ✅ **Better Performance**: Browser-native layer management
423
+ ✅ **Future-Proof**: Uses web platform standards
424
+ ✅ **Rich Features**: Built-in animations, backdrops, and light dismiss
425
+ ✅ **Type Safety**: Full TypeScript support with comprehensive prop types
426
+
427
+ ### Migration Checklist
428
+
429
+ - [ ] Replace `import { Popover } from '@fpkit/acss/hooks'` with `import { Popover } from '@fpkit/acss'`
430
+ - [ ] Replace `import { usePopover }` with the new Popover component
431
+ - [ ] Change `popoverTrigger` prop to `trigger`
432
+ - [ ] Move popover content to `children` prop
433
+ - [ ] Add unique `id` prop (recommended but optional)
434
+ - [ ] Choose `mode="auto"` (default) or `mode="manual"`
435
+ - [ ] Test browser compatibility (Chrome 125+, Safari 17.4+, Edge 125+)
436
+ - [ ] Update any custom styling to use CSS custom properties
437
+ - [ ] Test keyboard navigation (Escape key, Tab order)
438
+ - [ ] Verify accessibility with screen readers
439
+
440
+ ### Fallback for Older Browsers
441
+
442
+ The native Popover API is not supported in older browsers. Consider:
443
+
444
+ 1. **Feature Detection**: Check for popover support before using
445
+ 2. **Polyfill**: Use a popover polyfill for older browser support
446
+ 3. **Progressive Enhancement**: Provide fallback UI for unsupported browsers
447
+
448
+ ```tsx
449
+ // Feature detection example
450
+ if (!HTMLElement.prototype.hasOwnProperty('popover')) {
451
+ console.warn('Popover API not supported - consider polyfill');
452
+ }
453
+ ```
454
+
455
+ ### Need Help?
456
+
457
+ - Review the **Examples** section above for common use cases
458
+ - Check the **API Reference** for complete prop documentation
459
+ - See the **Styling** page for theming and customization options
460
+ - Open an issue on GitHub if you encounter migration problems
461
+
462
+ ## Troubleshooting
463
+
464
+ ### Popover not appearing
465
+
466
+ Check browser support and console for errors about popover API.
467
+
468
+ ### Positioning issues
469
+
470
+ CSS anchor positioning requires Chrome 125+. Older browsers use fallback positioning.
471
+
472
+ ### Animation not working
473
+
474
+ Ensure `@starting-style` is supported or provide fallback transitions.
475
+
476
+ ### Close button not showing
477
+
478
+ For auto mode, close button is hidden by default. Set `showCloseButton={true}` to show.