@duro-app/ui 5.6.0 → 5.7.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 (60) hide show
  1. package/dist/{Table-B1sWL3GA.js → Table-Dj7PXqO_.js} +3 -2
  2. package/dist/Table-Dj7PXqO_.js.map +1 -0
  3. package/dist/components/DragDrop/DragDrop.d.ts +95 -14
  4. package/dist/components/DragDrop/DragDrop.d.ts.map +1 -1
  5. package/dist/components/DragDrop/DragDrop.meta.d.ts.map +1 -1
  6. package/dist/components/DragDrop/DragDropBoard.stories.d.ts +69 -0
  7. package/dist/components/DragDrop/DragDropBoard.stories.d.ts.map +1 -0
  8. package/dist/components/DragDrop/styles.css.d.ts +80 -3
  9. package/dist/components/DragDrop/styles.css.d.ts.map +1 -1
  10. package/dist/components/Meter/Meter.d.ts +17 -0
  11. package/dist/components/Meter/Meter.d.ts.map +1 -0
  12. package/dist/components/Meter/Meter.meta.d.ts +3 -0
  13. package/dist/components/Meter/Meter.meta.d.ts.map +1 -0
  14. package/dist/components/Meter/Meter.stories.d.ts +12 -0
  15. package/dist/components/Meter/Meter.stories.d.ts.map +1 -0
  16. package/dist/components/Meter/styles.css.d.ts +34 -0
  17. package/dist/components/Meter/styles.css.d.ts.map +1 -0
  18. package/dist/components/Timeline/Timeline.d.ts +71 -0
  19. package/dist/components/Timeline/Timeline.d.ts.map +1 -0
  20. package/dist/components/Timeline/Timeline.meta.d.ts +3 -0
  21. package/dist/components/Timeline/Timeline.meta.d.ts.map +1 -0
  22. package/dist/components/Timeline/Timeline.stories.d.ts +26 -0
  23. package/dist/components/Timeline/Timeline.stories.d.ts.map +1 -0
  24. package/dist/components/Timeline/styles.css.d.ts +158 -0
  25. package/dist/components/Timeline/styles.css.d.ts.map +1 -0
  26. package/dist/components/Toast/Toast.meta.d.ts.map +1 -1
  27. package/dist/components/Toast/Toast.stories.d.ts +9 -0
  28. package/dist/components/Toast/Toast.stories.d.ts.map +1 -1
  29. package/dist/components/Toast/ToastProvider.d.ts +11 -5
  30. package/dist/components/Toast/ToastProvider.d.ts.map +1 -1
  31. package/dist/components/Toast/index.d.ts +1 -1
  32. package/dist/components/Toast/index.d.ts.map +1 -1
  33. package/dist/components/Toast/styles.css.d.ts +6 -0
  34. package/dist/components/Toast/styles.css.d.ts.map +1 -1
  35. package/dist/index.css +1 -1
  36. package/dist/index.d.ts +4 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +5029 -3927
  39. package/dist/index.js.map +1 -1
  40. package/dist/table.js +1 -1
  41. package/package.json +2 -2
  42. package/src/components/DragDrop/DragDrop.meta.ts +22 -21
  43. package/src/components/DragDrop/DragDrop.tsx +1147 -246
  44. package/src/components/DragDrop/DragDropBoard.stories.tsx +668 -0
  45. package/src/components/DragDrop/styles.css.ts +74 -4
  46. package/src/components/Meter/Meter.meta.ts +24 -0
  47. package/src/components/Meter/Meter.stories.tsx +64 -0
  48. package/src/components/Meter/Meter.tsx +55 -0
  49. package/src/components/Meter/styles.css.ts +30 -0
  50. package/src/components/Timeline/Timeline.meta.ts +46 -0
  51. package/src/components/Timeline/Timeline.stories.tsx +238 -0
  52. package/src/components/Timeline/Timeline.tsx +332 -0
  53. package/src/components/Timeline/styles.css.ts +162 -0
  54. package/src/components/Toast/Toast.meta.ts +2 -1
  55. package/src/components/Toast/Toast.stories.tsx +84 -0
  56. package/src/components/Toast/ToastProvider.tsx +49 -11
  57. package/src/components/Toast/index.ts +7 -1
  58. package/src/components/Toast/styles.css.ts +2 -0
  59. package/src/index.ts +16 -0
  60. package/dist/Table-B1sWL3GA.js.map +0 -1
@@ -5,6 +5,7 @@ import {
5
5
  useEffect,
6
6
  useMemo,
7
7
  useRef,
8
+ useState,
8
9
  useSyncExternalStore,
9
10
  type ReactNode,
10
11
  } from 'react'
@@ -14,7 +15,7 @@ import {Icon, type IconName} from '../Icon'
14
15
  import {usePortalMount} from '../ThemeProvider/ThemeProvider'
15
16
  import {styles} from './styles.css'
16
17
 
17
- export type ToastVariant = 'success' | 'error' | 'info'
18
+ export type ToastVariant = 'success' | 'error' | 'info' | 'warning'
18
19
 
19
20
  export interface ToastAction {
20
21
  label: string
@@ -22,12 +23,14 @@ export interface ToastAction {
22
23
  }
23
24
 
24
25
  export interface ToastOptions {
25
- /** Visual + semantic tone. Errors announce assertively and linger longer. */
26
+ /** Visual + semantic tone. Errors announce assertively; errors and
27
+ * warnings linger longer. */
26
28
  variant?: ToastVariant
27
29
  message: ReactNode
28
30
  /** Optional single action, e.g. Undo. Runs, then dismisses the toast. */
29
31
  action?: ToastAction
30
- /** Auto-dismiss delay in ms. `0` (or negative) keeps it until dismissed. */
32
+ /** Auto-dismiss delay in ms. `0` (or negative) keeps it until dismissed.
33
+ * The countdown pauses while the toast is hovered or holds focus. */
31
34
  duration?: number
32
35
  }
33
36
 
@@ -56,6 +59,7 @@ const DEFAULT_DURATION: Record<ToastVariant, number> = {
56
59
  success: 5000,
57
60
  info: 5000,
58
61
  error: 8000,
62
+ warning: 8000,
59
63
  }
60
64
 
61
65
  function createToastStore(): ToastStore {
@@ -92,11 +96,13 @@ const variantIcon: Record<ToastVariant, IconName> = {
92
96
  success: 'check-circle-filled',
93
97
  error: 'x-circle-filled',
94
98
  info: 'info-circle-filled',
99
+ warning: 'alert-triangle-filled',
95
100
  }
96
101
  const variantIconStyle = {
97
102
  success: styles.iconSuccess,
98
103
  error: styles.iconError,
99
104
  info: styles.iconInfo,
105
+ warning: styles.iconWarning,
100
106
  } as const
101
107
 
102
108
  const ToastContext = createContext<ToastContextValue | null>(null)
@@ -107,20 +113,45 @@ export function useToast(): ToastContextValue {
107
113
  return ctx
108
114
  }
109
115
 
110
- function ToastItem({entry, onDismiss}: {entry: ToastEntry; onDismiss: (id: string) => void}) {
116
+ function ToastItem({
117
+ entry,
118
+ onDismiss,
119
+ dismissLabel,
120
+ }: {
121
+ entry: ToastEntry
122
+ onDismiss: (id: string) => void
123
+ dismissLabel: string
124
+ }) {
111
125
  const {id, variant, message, action, duration} = entry
126
+ const [hovered, setHovered] = useState(false)
127
+ const [focused, setFocused] = useState(false)
128
+ const paused = hovered || focused
129
+ // What is left of the countdown; a pause keeps it, a resume restarts the
130
+ // timer with what was left (WCAG 2.2.1: the reader controls the timing).
131
+ const remaining = useRef(duration)
112
132
 
113
133
  useEffect(() => {
114
- if (duration <= 0) return
115
- const timer = setTimeout(() => onDismiss(id), duration)
116
- return () => clearTimeout(timer)
117
- }, [id, duration, onDismiss])
134
+ if (duration <= 0 || paused) return
135
+ const started = Date.now()
136
+ const timer = setTimeout(() => onDismiss(id), Math.max(remaining.current, 0))
137
+ return () => {
138
+ clearTimeout(timer)
139
+ remaining.current -= Date.now() - started
140
+ }
141
+ }, [id, duration, paused, onDismiss])
118
142
 
119
143
  return (
120
144
  <html.div
121
145
  role={variant === 'error' ? 'alert' : 'status'}
122
146
  aria-live={variant === 'error' ? 'assertive' : 'polite'}
123
147
  style={[styles.toast, styles[variant]]}
148
+ onPointerEnter={() => setHovered(true)}
149
+ onPointerLeave={() => setHovered(false)}
150
+ onFocus={() => setFocused(true)}
151
+ onBlur={(e: {currentTarget: HTMLElement; relatedTarget: EventTarget | null}) => {
152
+ // Focus moving between the toast's own buttons keeps it paused.
153
+ if (!e.currentTarget.contains(e.relatedTarget as Node | null)) setFocused(false)
154
+ }}
124
155
  >
125
156
  <html.div style={[styles.iconWrap, variantIconStyle[variant]]}>
126
157
  <Icon name={variantIcon[variant]} size="md" />
@@ -142,7 +173,7 @@ function ToastItem({entry, onDismiss}: {entry: ToastEntry; onDismiss: (id: strin
142
173
  </html.div>
143
174
  <html.button
144
175
  type="button"
145
- aria-label="Dismiss"
176
+ aria-label={dismissLabel}
146
177
  style={styles.closeBtn}
147
178
  onClick={() => onDismiss(id)}
148
179
  >
@@ -152,7 +183,14 @@ function ToastItem({entry, onDismiss}: {entry: ToastEntry; onDismiss: (id: strin
152
183
  )
153
184
  }
154
185
 
155
- export function ToastProvider({children}: {children: ReactNode}) {
186
+ export interface ToastProviderProps {
187
+ /** Accessible name of each toast's close button, e.g. from the app's
188
+ * message catalog. */
189
+ dismissLabel?: string
190
+ children: ReactNode
191
+ }
192
+
193
+ export function ToastProvider({dismissLabel = 'Dismiss', children}: ToastProviderProps) {
156
194
  const storeRef = useRef<ToastStore>(null)
157
195
  if (storeRef.current === null) {
158
196
  storeRef.current = createToastStore()
@@ -175,7 +213,7 @@ export function ToastProvider({children}: {children: ReactNode}) {
175
213
  const region = (
176
214
  <html.div role="region" aria-label="Notifications" style={styles.region}>
177
215
  {entries.map((entry) => (
178
- <ToastItem key={entry.id} entry={entry} onDismiss={dismiss} />
216
+ <ToastItem key={entry.id} entry={entry} onDismiss={dismiss} dismissLabel={dismissLabel} />
179
217
  ))}
180
218
  </html.div>
181
219
  )
@@ -1,2 +1,8 @@
1
1
  export {ToastProvider, useToast} from './ToastProvider'
2
- export type {ToastVariant, ToastOptions, ToastAction, ToastContextValue} from './ToastProvider'
2
+ export type {
3
+ ToastVariant,
4
+ ToastOptions,
5
+ ToastAction,
6
+ ToastContextValue,
7
+ ToastProviderProps,
8
+ } from './ToastProvider'
@@ -62,6 +62,7 @@ export const styles = css.create({
62
62
  success: {borderLeftColor: colors.successBorder},
63
63
  error: {borderLeftColor: colors.errorBorder},
64
64
  info: {borderLeftColor: colors.infoBorder},
65
+ warning: {borderLeftColor: colors.warningBorder},
65
66
 
66
67
  iconWrap: {
67
68
  flexShrink: 0,
@@ -73,6 +74,7 @@ export const styles = css.create({
73
74
  iconSuccess: {color: colors.successText},
74
75
  iconError: {color: colors.errorText},
75
76
  iconInfo: {color: colors.infoText},
77
+ iconWarning: {color: colors.warningText},
76
78
 
77
79
  content: {
78
80
  flex: 1,
package/src/index.ts CHANGED
@@ -25,9 +25,15 @@ export {ConfirmDialog} from './components/ConfirmDialog/ConfirmDialog'
25
25
  export {
26
26
  DragDrop,
27
27
  useDragDrop,
28
+ type DragDropAnnouncement,
28
29
  type DragDropEvent,
29
30
  type DragDropItemData,
31
+ type DragDropOverEvent,
32
+ type DragDropPlaceholderInfo,
33
+ type DragDropRefusal,
34
+ type DragDropRenderPlaceholder,
30
35
  type DragDropTarget,
36
+ type DragDropVerdict,
31
37
  } from './components/DragDrop/DragDrop'
32
38
  export {Dialog, type DialogSize} from './components/Dialog/Dialog'
33
39
  export {DetailPanel, type DetailPanelSize} from './components/DetailPanel/DetailPanel'
@@ -46,6 +52,7 @@ export {
46
52
  type LinkButtonSize,
47
53
  } from './components/LinkButton/LinkButton'
48
54
  export {Menu} from './components/Menu/Menu'
55
+ export {Meter, type MeterProps, type MeterTone} from './components/Meter/Meter'
49
56
  export {PageNav} from './components/PageNav/PageNav'
50
57
  export {Panel} from './components/Panel/Panel'
51
58
  export {RadioGroup} from './components/RadioGroup/RadioGroup'
@@ -65,6 +72,7 @@ export {
65
72
  type ToastOptions,
66
73
  type ToastAction,
67
74
  type ToastContextValue,
75
+ type ToastProviderProps,
68
76
  } from './components/Toast'
69
77
  export {
70
78
  StatusIcon,
@@ -84,6 +92,14 @@ export {
84
92
  type TableOfContentsVariant,
85
93
  } from './components/TableOfContents/TableOfContents'
86
94
  export {Tabs} from './components/Tabs/Tabs'
95
+ export {
96
+ Timeline,
97
+ type TimelineBarDropZone,
98
+ type TimelineBarProps,
99
+ type TimelineDate,
100
+ type TimelineRootProps,
101
+ type TimelineRowProps,
102
+ } from './components/Timeline/Timeline'
87
103
  export {Tree} from './components/Tree/Tree'
88
104
  export {Textarea, type TextareaVariant} from './components/Textarea/Textarea'
89
105
  export {