@hadoku/task-ui-components 0.0.0-stage → 4.20.3

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 (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +917 -2
  3. package/dist/app-header.css +288 -0
  4. package/dist/bento.css +143 -0
  5. package/dist/button.css +227 -0
  6. package/dist/components/AppHeader.d.ts +61 -0
  7. package/dist/components/AppHeader.d.ts.map +1 -0
  8. package/dist/components/AppHeader.js +23 -0
  9. package/dist/components/AppHeader.js.map +1 -0
  10. package/dist/components/BentoCard.d.ts +23 -0
  11. package/dist/components/BentoCard.d.ts.map +1 -0
  12. package/dist/components/BentoCard.js +49 -0
  13. package/dist/components/BentoCard.js.map +1 -0
  14. package/dist/components/BentoGrid.d.ts +24 -0
  15. package/dist/components/BentoGrid.d.ts.map +1 -0
  16. package/dist/components/BentoGrid.js +22 -0
  17. package/dist/components/BentoGrid.js.map +1 -0
  18. package/dist/components/Button.d.ts +28 -0
  19. package/dist/components/Button.d.ts.map +1 -0
  20. package/dist/components/Button.js +51 -0
  21. package/dist/components/Button.js.map +1 -0
  22. package/dist/components/ConnectedSettings.d.ts +53 -0
  23. package/dist/components/ConnectedSettings.d.ts.map +1 -0
  24. package/dist/components/ConnectedSettings.js +302 -0
  25. package/dist/components/ConnectedSettings.js.map +1 -0
  26. package/dist/components/ConnectedThemePicker.d.ts +52 -0
  27. package/dist/components/ConnectedThemePicker.d.ts.map +1 -0
  28. package/dist/components/ConnectedThemePicker.js +32 -0
  29. package/dist/components/ConnectedThemePicker.js.map +1 -0
  30. package/dist/components/ContextMenu.d.ts +22 -0
  31. package/dist/components/ContextMenu.d.ts.map +1 -0
  32. package/dist/components/ContextMenu.js +23 -0
  33. package/dist/components/ContextMenu.js.map +1 -0
  34. package/dist/components/HadokuThemePicker.d.ts +27 -0
  35. package/dist/components/HadokuThemePicker.d.ts.map +1 -0
  36. package/dist/components/HadokuThemePicker.js +19 -0
  37. package/dist/components/HadokuThemePicker.js.map +1 -0
  38. package/dist/components/LoadingSkeleton.d.ts +14 -0
  39. package/dist/components/LoadingSkeleton.d.ts.map +1 -0
  40. package/dist/components/LoadingSkeleton.js +62 -0
  41. package/dist/components/LoadingSkeleton.js.map +1 -0
  42. package/dist/components/Modal.d.ts +39 -0
  43. package/dist/components/Modal.d.ts.map +1 -0
  44. package/dist/components/Modal.js +106 -0
  45. package/dist/components/Modal.js.map +1 -0
  46. package/dist/components/NumberInput.d.ts +26 -0
  47. package/dist/components/NumberInput.d.ts.map +1 -0
  48. package/dist/components/NumberInput.js +131 -0
  49. package/dist/components/NumberInput.js.map +1 -0
  50. package/dist/components/Select.d.ts +52 -0
  51. package/dist/components/Select.d.ts.map +1 -0
  52. package/dist/components/Select.js +281 -0
  53. package/dist/components/Select.js.map +1 -0
  54. package/dist/components/SettingsModal.d.ts +69 -0
  55. package/dist/components/SettingsModal.d.ts.map +1 -0
  56. package/dist/components/SettingsModal.js +141 -0
  57. package/dist/components/SettingsModal.js.map +1 -0
  58. package/dist/components/ThemeIcons.d.ts +26 -0
  59. package/dist/components/ThemeIcons.d.ts.map +1 -0
  60. package/dist/components/ThemeIcons.js +74 -0
  61. package/dist/components/ThemeIcons.js.map +1 -0
  62. package/dist/components/ThemePicker.d.ts +28 -0
  63. package/dist/components/ThemePicker.d.ts.map +1 -0
  64. package/dist/components/ThemePicker.js +101 -0
  65. package/dist/components/ThemePicker.js.map +1 -0
  66. package/dist/components/Toast.d.ts +13 -0
  67. package/dist/components/Toast.d.ts.map +1 -0
  68. package/dist/components/Toast.js +70 -0
  69. package/dist/components/Toast.js.map +1 -0
  70. package/dist/components/Toaster.d.ts +19 -0
  71. package/dist/components/Toaster.d.ts.map +1 -0
  72. package/dist/components/Toaster.js +19 -0
  73. package/dist/components/Toaster.js.map +1 -0
  74. package/dist/components/Tooltip.d.ts +49 -0
  75. package/dist/components/Tooltip.d.ts.map +1 -0
  76. package/dist/components/Tooltip.js +143 -0
  77. package/dist/components/Tooltip.js.map +1 -0
  78. package/dist/context-menu.css +11 -0
  79. package/dist/controls.css +137 -0
  80. package/dist/hooks/useReducedMotion.d.ts +7 -0
  81. package/dist/hooks/useReducedMotion.d.ts.map +1 -0
  82. package/dist/hooks/useReducedMotion.js +25 -0
  83. package/dist/hooks/useReducedMotion.js.map +1 -0
  84. package/dist/hooks/useToast.d.ts +18 -0
  85. package/dist/hooks/useToast.d.ts.map +1 -0
  86. package/dist/hooks/useToast.js +28 -0
  87. package/dist/hooks/useToast.js.map +1 -0
  88. package/dist/icon-btn.css +64 -0
  89. package/dist/index.d.ts +49 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +72 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/lib/menuPlacement.d.ts +16 -0
  94. package/dist/lib/menuPlacement.d.ts.map +1 -0
  95. package/dist/lib/menuPlacement.js +19 -0
  96. package/dist/lib/menuPlacement.js.map +1 -0
  97. package/dist/lib/selectMenus.d.ts +30 -0
  98. package/dist/lib/selectMenus.d.ts.map +1 -0
  99. package/dist/lib/selectMenus.js +236 -0
  100. package/dist/lib/selectMenus.js.map +1 -0
  101. package/dist/lib/settingsClient.d.ts +120 -0
  102. package/dist/lib/settingsClient.d.ts.map +1 -0
  103. package/dist/lib/settingsClient.js +140 -0
  104. package/dist/lib/settingsClient.js.map +1 -0
  105. package/dist/lib/themeScope.d.ts +19 -0
  106. package/dist/lib/themeScope.d.ts.map +1 -0
  107. package/dist/lib/themeScope.js +10 -0
  108. package/dist/lib/themeScope.js.map +1 -0
  109. package/dist/lib/titleTooltips.d.ts +38 -0
  110. package/dist/lib/titleTooltips.d.ts.map +1 -0
  111. package/dist/lib/titleTooltips.js +166 -0
  112. package/dist/lib/titleTooltips.js.map +1 -0
  113. package/dist/lib/tooltipPlacement.d.ts +14 -0
  114. package/dist/lib/tooltipPlacement.d.ts.map +1 -0
  115. package/dist/lib/tooltipPlacement.js +34 -0
  116. package/dist/lib/tooltipPlacement.js.map +1 -0
  117. package/dist/modal.css +129 -0
  118. package/dist/number-input.css +131 -0
  119. package/dist/scrollbar.css +165 -0
  120. package/dist/select.css +251 -0
  121. package/dist/styles.css +1866 -0
  122. package/dist/task-ui-components.css +1866 -0
  123. package/dist/theme-picker.css +219 -0
  124. package/dist/toaster.css +159 -0
  125. package/dist/tooltip.css +103 -0
  126. package/dist/types/index.d.ts +37 -0
  127. package/dist/types/index.d.ts.map +1 -0
  128. package/package.json +60 -4
  129. package/src/app-header.css +288 -0
  130. package/src/bento.css +143 -0
  131. package/src/button.css +227 -0
  132. package/src/components/AppHeader.tsx +87 -0
  133. package/src/components/BentoCard.tsx +72 -0
  134. package/src/components/BentoGrid.tsx +48 -0
  135. package/src/components/Button.tsx +81 -0
  136. package/src/components/ConnectedSettings.tsx +465 -0
  137. package/src/components/ConnectedThemePicker.tsx +79 -0
  138. package/src/components/ContextMenu.tsx +48 -0
  139. package/src/components/HadokuThemePicker.tsx +39 -0
  140. package/src/components/LoadingSkeleton.tsx +84 -0
  141. package/src/components/Modal.tsx +171 -0
  142. package/src/components/NumberInput.tsx +161 -0
  143. package/src/components/Select.tsx +359 -0
  144. package/src/components/SettingsModal.tsx +235 -0
  145. package/src/components/ThemeIcons.tsx +127 -0
  146. package/src/components/ThemePicker.tsx +149 -0
  147. package/src/components/Toast.tsx +92 -0
  148. package/src/components/Toaster.tsx +43 -0
  149. package/src/components/Tooltip.tsx +212 -0
  150. package/src/context-menu.css +11 -0
  151. package/src/controls.css +137 -0
  152. package/src/hooks/useReducedMotion.ts +36 -0
  153. package/src/hooks/useToast.ts +57 -0
  154. package/src/icon-btn.css +64 -0
  155. package/src/index.ts +140 -0
  156. package/src/lib/menuPlacement.ts +31 -0
  157. package/src/lib/selectMenus.ts +303 -0
  158. package/src/lib/settingsClient.ts +377 -0
  159. package/src/lib/themeScope.ts +23 -0
  160. package/src/lib/titleTooltips.ts +236 -0
  161. package/src/lib/tooltipPlacement.ts +59 -0
  162. package/src/modal.css +129 -0
  163. package/src/number-input.css +131 -0
  164. package/src/scrollbar.css +165 -0
  165. package/src/select.css +251 -0
  166. package/src/theme-picker.css +219 -0
  167. package/src/toaster.css +159 -0
  168. package/src/tooltip.css +103 -0
  169. package/src/types/index.ts +43 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * AppHeader — the ecosystem-standard top bar for every hadoku.me app.
3
+ *
4
+ * Enforces the shared contract so child apps stop drifting:
5
+ * [ title (left) ] ————— <space> ————— [ status? · ThemePicker · Settings gear ] (right)
6
+ *
7
+ * Everything BELOW the header is the app's own concern. The header owns the
8
+ * title, and it OWNS both right-hand controls outright — it renders the theme
9
+ * picker and the settings gear itself. Neither is a prop.
10
+ *
11
+ * WHY THEY ARE NOT PROPS ANY MORE
12
+ * -------------------------------
13
+ * `themePicker?: React.ReactNode` was an open hole: a slot that accepts a node
14
+ * accepts ANY node, so "every app uses the same picker" was a convention, and
15
+ * conventions drift. They did — one app passed a raw `<ThemePicker>` with its
16
+ * own `isOpen` state and a debug logger wired into `getThemeIcon`, and eight
17
+ * apps each kept a hand-copied `useTheme` hook to feed it. Those copies had
18
+ * already diverged on real behaviour (only one wrote `localStorage`, so the
19
+ * rest lost the theme on browser restart and flashed a default on next paint).
20
+ *
21
+ * The control is now rendered here, from `useHadokuTheme()`. An app cannot
22
+ * substitute, restyle or omit it. What an app CAN do is inject its own
23
+ * preferences — see `children`.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * import { AppHeader } from '@wolffm/task-ui-components'
28
+ * import { HadokuThemeRoot } from '@wolffm/themes'
29
+ *
30
+ * // once, at the app root:
31
+ * <HadokuThemeRoot>
32
+ * <AppHeader title="Print Tool" status={<ApiStatus status={apiStatus} />}>
33
+ * <MyAppPreferences /> // optional: appended AFTER the canonical rows
34
+ * </AppHeader>
35
+ * </HadokuThemeRoot>
36
+ * ```
37
+ */
38
+ import React from 'react'
39
+ import { ConnectedSettings, type ConnectedSettingsProps } from './ConnectedSettings'
40
+ import { HadokuThemePicker } from './HadokuThemePicker'
41
+
42
+ export interface AppHeaderProps {
43
+ /** Left-aligned header title. A string is wrapped in an <h1>; pass a node for
44
+ * custom markup (e.g. a clickable title button). */
45
+ title: React.ReactNode
46
+ /** Optional status/extra node shown left of the theme + settings controls
47
+ * (e.g. an "API Online" indicator). Acceptable per the header contract. */
48
+ status?: React.ReactNode
49
+ /** Identity hints for the settings popout. Narrow on purpose: `userType` and
50
+ * `name` exist because an app behind a path-prefixed shim may not be able to
51
+ * reach `/session/whoami` (conjure), NOT as a styling or layout hook. */
52
+ settingsProps?: ConnectedSettingsProps
53
+ /** THE app-specific extension point. Rendered inside the settings popout as a
54
+ * final section, under the four canonical rows. Put your app's own
55
+ * preferences here — checkboxes, selects, whatever it needs; this slot is
56
+ * deliberately unconstrained. What it cannot do is alter the rows above it. */
57
+ children?: React.ReactNode
58
+ /** Extra class on the <header> root. Layout only — it cannot reach the
59
+ * controls, which own their own markup. */
60
+ className?: string
61
+ }
62
+
63
+ export function AppHeader({
64
+ title,
65
+ status,
66
+ settingsProps,
67
+ children,
68
+ className = ''
69
+ }: AppHeaderProps) {
70
+ // No useHadokuTheme() call here: <HadokuThemePicker> reads the context and
71
+ // throws its own actionable error when the app root was never wrapped, so a
72
+ // second read would only duplicate that.
73
+ return (
74
+ <header className={`app-header ${className}`.trim()}>
75
+ {typeof title === 'string' ? (
76
+ <h1 className="app-header__title">{title}</h1>
77
+ ) : (
78
+ <div className="app-header__title">{title}</div>
79
+ )}
80
+ <div className="app-header__actions">
81
+ {status}
82
+ <HadokuThemePicker />
83
+ <ConnectedSettings {...settingsProps}>{children}</ConnectedSettings>
84
+ </div>
85
+ </header>
86
+ )
87
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * BentoCard Component
3
+ * Base card component for bento grid layouts
4
+ * Supports various sizes and disabled state for preloading
5
+ */
6
+
7
+ import React from 'react'
8
+
9
+ export type CardSize = 'default' | 'wide' | 'tall' | 'large' | 'hero'
10
+
11
+ export interface BentoCardProps {
12
+ children: React.ReactNode
13
+ className?: string
14
+ /** Card size affects grid span */
15
+ size?: CardSize
16
+ /** Disable interactions (for preload state) */
17
+ disabled?: boolean
18
+ /** Click handler */
19
+ onClick?: () => void
20
+ /** Custom aria-label for accessibility */
21
+ ariaLabel?: string
22
+ /** Optional data attributes for animation libraries */
23
+ 'data-layout-id'?: string
24
+ }
25
+
26
+ const sizeClasses: Record<CardSize, string> = {
27
+ default: 'bento-card--default',
28
+ wide: 'bento-card--wide',
29
+ tall: 'bento-card--tall',
30
+ large: 'bento-card--large',
31
+ hero: 'bento-card--hero'
32
+ }
33
+
34
+ export function BentoCard({
35
+ children,
36
+ className = '',
37
+ size = 'default',
38
+ disabled = false,
39
+ onClick,
40
+ ariaLabel,
41
+ 'data-layout-id': layoutId,
42
+ ...rest
43
+ }: BentoCardProps) {
44
+ const handleClick = () => {
45
+ if (!disabled && onClick) {
46
+ onClick()
47
+ }
48
+ }
49
+
50
+ const handleKeyDown = (e: React.KeyboardEvent) => {
51
+ if ((e.key === 'Enter' || e.key === ' ') && !disabled && onClick) {
52
+ e.preventDefault()
53
+ onClick()
54
+ }
55
+ }
56
+
57
+ return (
58
+ <article
59
+ className={`bento-card ${sizeClasses[size]} ${disabled ? 'bento-card--disabled' : ''} ${className}`}
60
+ onClick={handleClick}
61
+ onKeyDown={handleKeyDown}
62
+ role={onClick ? 'button' : undefined}
63
+ tabIndex={onClick && !disabled ? 0 : undefined}
64
+ aria-label={ariaLabel}
65
+ aria-disabled={disabled}
66
+ data-layout-id={layoutId}
67
+ {...rest}
68
+ >
69
+ {children}
70
+ </article>
71
+ )
72
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * BentoGrid Component
3
+ * Responsive grid container for bento-style card layouts
4
+ * Automatically adjusts columns based on viewport size
5
+ */
6
+
7
+ import React from 'react'
8
+
9
+ export type BentoColumns = 1 | 2 | 3 | 4 | 6
10
+
11
+ export interface BentoGridProps {
12
+ children: React.ReactNode
13
+ className?: string
14
+ /** Column configuration at each breakpoint */
15
+ columns?: {
16
+ sm?: BentoColumns
17
+ md?: BentoColumns
18
+ lg?: BentoColumns
19
+ xl?: BentoColumns
20
+ }
21
+ /** Gap between cards in rem */
22
+ gap?: number
23
+ /** Maximum width of the grid */
24
+ maxWidth?: string
25
+ }
26
+
27
+ export function BentoGrid({
28
+ children,
29
+ className = '',
30
+ columns = { sm: 2, md: 3, lg: 4, xl: 6 },
31
+ gap = 1,
32
+ maxWidth = '1400px'
33
+ }: BentoGridProps) {
34
+ const gridStyle: React.CSSProperties = {
35
+ '--bento-gap': `${gap}rem`,
36
+ '--bento-max-width': maxWidth,
37
+ '--bento-cols-sm': columns.sm ?? 2,
38
+ '--bento-cols-md': columns.md ?? 3,
39
+ '--bento-cols-lg': columns.lg ?? 4,
40
+ '--bento-cols-xl': columns.xl ?? 6
41
+ } as React.CSSProperties
42
+
43
+ return (
44
+ <div className={`bento-grid ${className}`} style={gridStyle}>
45
+ {children}
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Button Component
3
+ * Themed button in five treatments x five colour families, so an app never
4
+ * falls back to the browser's default button chrome.
5
+ */
6
+
7
+ import React from 'react'
8
+ import { Icon, type IconName } from '@wolffm/themes'
9
+ import '../button.css'
10
+
11
+ export type ButtonVariant = 'solid' | 'gradient' | 'soft' | 'outline' | 'ghost'
12
+ export type ButtonFamily = 'primary' | 'success' | 'warning' | 'danger' | 'neutral'
13
+ export type ButtonSize = 'sm' | 'md' | 'lg'
14
+ export type ButtonShape = 'rounded' | 'pill' | 'square'
15
+
16
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
17
+ variant?: ButtonVariant
18
+ family?: ButtonFamily
19
+ size?: ButtonSize
20
+ shape?: ButtonShape
21
+ /** Registry icon before the label. */
22
+ icon?: IconName
23
+ /** Registry icon after the label (e.g. `chevron-down` on a menu button). */
24
+ iconEnd?: IconName
25
+ /** Shows a spinner, sets aria-busy and blocks clicks. */
26
+ loading?: boolean
27
+ /** Stretch to the container width. */
28
+ block?: boolean
29
+ }
30
+
31
+ const ICON_SIZE: Record<ButtonSize, number> = { sm: 14, md: 16, lg: 18 }
32
+
33
+ export function Button({
34
+ variant = 'soft',
35
+ family = 'primary',
36
+ size = 'md',
37
+ shape = 'rounded',
38
+ icon,
39
+ iconEnd,
40
+ loading = false,
41
+ block = false,
42
+ disabled,
43
+ type = 'button',
44
+ className,
45
+ children,
46
+ ...rest
47
+ }: ButtonProps) {
48
+ // An icon with no label is a square button; it still needs an aria-label.
49
+ const iconOnly = !children && (icon || iconEnd)
50
+ const classes = [
51
+ 'hdk-btn',
52
+ `hdk-btn--${variant}`,
53
+ `hdk-btn--${family}`,
54
+ `hdk-btn--${size}`,
55
+ `hdk-btn--${shape}`,
56
+ iconOnly && 'hdk-btn--icon-only',
57
+ block && 'hdk-btn--block',
58
+ loading && 'hdk-btn--loading',
59
+ className
60
+ ]
61
+ .filter(Boolean)
62
+ .join(' ')
63
+
64
+ return (
65
+ <button
66
+ type={type}
67
+ className={classes}
68
+ disabled={disabled || loading}
69
+ aria-busy={loading || undefined}
70
+ {...rest}
71
+ >
72
+ {loading ? (
73
+ <span className="hdk-btn__spinner" aria-hidden="true" />
74
+ ) : (
75
+ icon && <Icon name={icon} size={ICON_SIZE[size]} />
76
+ )}
77
+ {children}
78
+ {iconEnd && <Icon name={iconEnd} size={ICON_SIZE[size]} />}
79
+ </button>
80
+ )
81
+ }