@hadoku/task-ui-components 0.0.0-stage → 4.20.4
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/LICENSE +21 -0
- package/README.md +917 -2
- package/dist/app-header.css +288 -0
- package/dist/bento.css +143 -0
- package/dist/button.css +227 -0
- package/dist/components/AppHeader.d.ts +61 -0
- package/dist/components/AppHeader.d.ts.map +1 -0
- package/dist/components/AppHeader.js +23 -0
- package/dist/components/AppHeader.js.map +1 -0
- package/dist/components/BentoCard.d.ts +23 -0
- package/dist/components/BentoCard.d.ts.map +1 -0
- package/dist/components/BentoCard.js +49 -0
- package/dist/components/BentoCard.js.map +1 -0
- package/dist/components/BentoGrid.d.ts +24 -0
- package/dist/components/BentoGrid.d.ts.map +1 -0
- package/dist/components/BentoGrid.js +22 -0
- package/dist/components/BentoGrid.js.map +1 -0
- package/dist/components/Button.d.ts +28 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +51 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/ConnectedSettings.d.ts +53 -0
- package/dist/components/ConnectedSettings.d.ts.map +1 -0
- package/dist/components/ConnectedSettings.js +368 -0
- package/dist/components/ConnectedSettings.js.map +1 -0
- package/dist/components/ConnectedThemePicker.d.ts +52 -0
- package/dist/components/ConnectedThemePicker.d.ts.map +1 -0
- package/dist/components/ConnectedThemePicker.js +32 -0
- package/dist/components/ConnectedThemePicker.js.map +1 -0
- package/dist/components/ContextMenu.d.ts +22 -0
- package/dist/components/ContextMenu.d.ts.map +1 -0
- package/dist/components/ContextMenu.js +23 -0
- package/dist/components/ContextMenu.js.map +1 -0
- package/dist/components/HadokuThemePicker.d.ts +27 -0
- package/dist/components/HadokuThemePicker.d.ts.map +1 -0
- package/dist/components/HadokuThemePicker.js +19 -0
- package/dist/components/HadokuThemePicker.js.map +1 -0
- package/dist/components/LoadingSkeleton.d.ts +14 -0
- package/dist/components/LoadingSkeleton.d.ts.map +1 -0
- package/dist/components/LoadingSkeleton.js +62 -0
- package/dist/components/LoadingSkeleton.js.map +1 -0
- package/dist/components/Modal.d.ts +39 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +106 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/NumberInput.d.ts +26 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +131 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/Select.d.ts +52 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Select.js +281 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/SettingsModal.d.ts +69 -0
- package/dist/components/SettingsModal.d.ts.map +1 -0
- package/dist/components/SettingsModal.js +141 -0
- package/dist/components/SettingsModal.js.map +1 -0
- package/dist/components/ThemeIcons.d.ts +26 -0
- package/dist/components/ThemeIcons.d.ts.map +1 -0
- package/dist/components/ThemeIcons.js +74 -0
- package/dist/components/ThemeIcons.js.map +1 -0
- package/dist/components/ThemePicker.d.ts +28 -0
- package/dist/components/ThemePicker.d.ts.map +1 -0
- package/dist/components/ThemePicker.js +101 -0
- package/dist/components/ThemePicker.js.map +1 -0
- package/dist/components/Toast.d.ts +13 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +70 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/Toaster.d.ts +19 -0
- package/dist/components/Toaster.d.ts.map +1 -0
- package/dist/components/Toaster.js +19 -0
- package/dist/components/Toaster.js.map +1 -0
- package/dist/components/Tooltip.d.ts +49 -0
- package/dist/components/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip.js +143 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/context-menu.css +11 -0
- package/dist/controls.css +137 -0
- package/dist/hooks/useReducedMotion.d.ts +7 -0
- package/dist/hooks/useReducedMotion.d.ts.map +1 -0
- package/dist/hooks/useReducedMotion.js +25 -0
- package/dist/hooks/useReducedMotion.js.map +1 -0
- package/dist/hooks/useToast.d.ts +18 -0
- package/dist/hooks/useToast.d.ts.map +1 -0
- package/dist/hooks/useToast.js +28 -0
- package/dist/hooks/useToast.js.map +1 -0
- package/dist/icon-btn.css +64 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/menuPlacement.d.ts +16 -0
- package/dist/lib/menuPlacement.d.ts.map +1 -0
- package/dist/lib/menuPlacement.js +19 -0
- package/dist/lib/menuPlacement.js.map +1 -0
- package/dist/lib/selectMenus.d.ts +30 -0
- package/dist/lib/selectMenus.d.ts.map +1 -0
- package/dist/lib/selectMenus.js +236 -0
- package/dist/lib/selectMenus.js.map +1 -0
- package/dist/lib/settingsClient.d.ts +144 -0
- package/dist/lib/settingsClient.d.ts.map +1 -0
- package/dist/lib/settingsClient.js +157 -0
- package/dist/lib/settingsClient.js.map +1 -0
- package/dist/lib/themeScope.d.ts +19 -0
- package/dist/lib/themeScope.d.ts.map +1 -0
- package/dist/lib/themeScope.js +10 -0
- package/dist/lib/themeScope.js.map +1 -0
- package/dist/lib/titleTooltips.d.ts +38 -0
- package/dist/lib/titleTooltips.d.ts.map +1 -0
- package/dist/lib/titleTooltips.js +166 -0
- package/dist/lib/titleTooltips.js.map +1 -0
- package/dist/lib/tooltipPlacement.d.ts +14 -0
- package/dist/lib/tooltipPlacement.d.ts.map +1 -0
- package/dist/lib/tooltipPlacement.js +34 -0
- package/dist/lib/tooltipPlacement.js.map +1 -0
- package/dist/modal.css +129 -0
- package/dist/number-input.css +131 -0
- package/dist/scrollbar.css +165 -0
- package/dist/select.css +251 -0
- package/dist/styles.css +1866 -0
- package/dist/task-ui-components.css +1866 -0
- package/dist/theme-picker.css +219 -0
- package/dist/toaster.css +159 -0
- package/dist/tooltip.css +103 -0
- package/dist/types/index.d.ts +37 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +60 -4
- package/src/app-header.css +288 -0
- package/src/bento.css +143 -0
- package/src/button.css +227 -0
- package/src/components/AppHeader.tsx +87 -0
- package/src/components/BentoCard.tsx +72 -0
- package/src/components/BentoGrid.tsx +48 -0
- package/src/components/Button.tsx +81 -0
- package/src/components/ConnectedSettings.tsx +545 -0
- package/src/components/ConnectedThemePicker.tsx +79 -0
- package/src/components/ContextMenu.tsx +48 -0
- package/src/components/HadokuThemePicker.tsx +39 -0
- package/src/components/LoadingSkeleton.tsx +84 -0
- package/src/components/Modal.tsx +171 -0
- package/src/components/NumberInput.tsx +161 -0
- package/src/components/Select.tsx +359 -0
- package/src/components/SettingsModal.tsx +235 -0
- package/src/components/ThemeIcons.tsx +127 -0
- package/src/components/ThemePicker.tsx +149 -0
- package/src/components/Toast.tsx +92 -0
- package/src/components/Toaster.tsx +43 -0
- package/src/components/Tooltip.tsx +212 -0
- package/src/context-menu.css +11 -0
- package/src/controls.css +137 -0
- package/src/hooks/useReducedMotion.ts +36 -0
- package/src/hooks/useToast.ts +57 -0
- package/src/icon-btn.css +64 -0
- package/src/index.ts +153 -0
- package/src/lib/menuPlacement.ts +31 -0
- package/src/lib/selectMenus.ts +303 -0
- package/src/lib/settingsClient.ts +425 -0
- package/src/lib/themeScope.ts +23 -0
- package/src/lib/titleTooltips.ts +236 -0
- package/src/lib/tooltipPlacement.ts +59 -0
- package/src/modal.css +129 -0
- package/src/number-input.css +131 -0
- package/src/scrollbar.css +165 -0
- package/src/select.css +251 -0
- package/src/theme-picker.css +219 -0
- package/src/toaster.css +159 -0
- package/src/tooltip.css +103 -0
- package/src/types/index.ts +43 -0
package/src/controls.css
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* Themed fallbacks for the browser's own control styling, for
|
|
2
|
+
* @wolffm/task-ui-components.
|
|
3
|
+
*
|
|
4
|
+
* import '@wolffm/task-ui-components/controls.css'
|
|
5
|
+
* <html class="hdk-controls">
|
|
6
|
+
*
|
|
7
|
+
* hadoku_site puts `hdk-controls` on <html>, which covers every
|
|
8
|
+
* micro-frontend. Every selector is wrapped in `:where()`, so all of it has
|
|
9
|
+
* ZERO specificity: it only fills in where an app styled nothing. An app's
|
|
10
|
+
* own rule for the same property — any selector at all — wins, whatever order
|
|
11
|
+
* the sheets load in. An author rule of any specificity still beats the
|
|
12
|
+
* browser's stylesheet, which is all this needs to.
|
|
13
|
+
*
|
|
14
|
+
* What it covers, each the browser default it replaces:
|
|
15
|
+
* - focus ring Chrome's two-tone `outline: auto` -> the accent
|
|
16
|
+
* - links -webkit-link blue / visited purple -> the text around it
|
|
17
|
+
* - checkbox, radio, range, progress system blue -> accent-color
|
|
18
|
+
* - text selection system highlight -> the accent tint
|
|
19
|
+
* - placeholder darkgray -> tertiary text
|
|
20
|
+
* - <summary> marker text-colour triangle -> secondary text
|
|
21
|
+
* - file picker OS "Choose file" button -> a soft button
|
|
22
|
+
* - number spinners OS up/down arrows -> removed (arrow keys
|
|
23
|
+
* still step; <NumberInput> is the themed stepper)
|
|
24
|
+
* - text fields and textarea, buttons, a select's closed box: the system
|
|
25
|
+
* field/button chrome -> the theme's card surface and border
|
|
26
|
+
*
|
|
27
|
+
* The open list of a <select> is not CSS-reachable at all; installSelectMenus()
|
|
28
|
+
* replaces it. Date and time pickers stay OS-drawn: apps own those.
|
|
29
|
+
*
|
|
30
|
+
* Everything sits in `@layer hdk-fallbacks`. A rule in a cascade layer loses
|
|
31
|
+
* to every unlayered rule and to every layer declared after it — whatever
|
|
32
|
+
* either's specificity — and Tailwind ships its utilities in layers. Unlayered,
|
|
33
|
+
* even a zero-specificity rule here would beat `bg-primary`. The page must
|
|
34
|
+
* declare the layer FIRST, before any other, so it ranks lowest:
|
|
35
|
+
*
|
|
36
|
+
* @layer hdk-fallbacks, base, theme, utilities;
|
|
37
|
+
*
|
|
38
|
+
* hadoku_site's global.css does that. A sheet that never names it just gets
|
|
39
|
+
* these rules below its own unlayered ones, which is also correct.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
@layer hdk-fallbacks {
|
|
43
|
+
/* accent-color inherits, so it is declared once per theme scope rather than
|
|
44
|
+
* per element: an app's accent-color on a container still reaches its
|
|
45
|
+
* children, and a nested [data-theme] still resolves its own accent. */
|
|
46
|
+
:where(.hdk-controls),
|
|
47
|
+
:where(.hdk-controls [data-theme]) {
|
|
48
|
+
accent-color: var(--color-primary);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:where(.hdk-controls) :where(:focus-visible) {
|
|
52
|
+
outline: 2px solid var(--color-primary);
|
|
53
|
+
outline-offset: 2px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* The browser colours a link directly rather than letting it inherit, so a
|
|
57
|
+
* link nobody styled is blue on any surface. `inherit` is what an app means
|
|
58
|
+
* when it styles the surface and not the link (Tailwind's preflight does the
|
|
59
|
+
* same); the underline keeps it reading as a link. */
|
|
60
|
+
:where(.hdk-controls) :where(a:any-link) {
|
|
61
|
+
color: inherit;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:where(.hdk-controls) ::selection {
|
|
65
|
+
background-color: var(--color-primary-bg);
|
|
66
|
+
color: var(--color-on-primary-bg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
:where(.hdk-controls) ::placeholder {
|
|
70
|
+
color: var(--color-text-tertiary);
|
|
71
|
+
opacity: 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:where(.hdk-controls) :where(summary)::marker {
|
|
75
|
+
color: var(--color-text-secondary);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:where(.hdk-controls) :where(input[type='file'])::file-selector-button {
|
|
79
|
+
margin-inline-end: 8px;
|
|
80
|
+
padding: 4px 10px;
|
|
81
|
+
font: inherit;
|
|
82
|
+
font-weight: var(--font-weight-semibold);
|
|
83
|
+
color: var(--color-on-neutral-bg);
|
|
84
|
+
background-color: var(--color-neutral-bg);
|
|
85
|
+
border: 1px solid transparent;
|
|
86
|
+
border-radius: var(--hdk-radius);
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:where(.hdk-controls) :where(input[type='file'])::file-selector-button:hover {
|
|
91
|
+
border-color: var(--color-neutral);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
:where(.hdk-controls) :where(input[type='number']) {
|
|
95
|
+
appearance: textfield;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
:where(.hdk-controls) :where(input[type='number'])::-webkit-inner-spin-button,
|
|
99
|
+
:where(.hdk-controls) :where(input[type='number'])::-webkit-outer-spin-button {
|
|
100
|
+
appearance: none;
|
|
101
|
+
margin: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Field chrome: only the properties that make up the system look. No padding,
|
|
105
|
+
* radius, font or size: those decide layout and shape, and an app that
|
|
106
|
+
* styled a field's background but not its padding must not see it move. */
|
|
107
|
+
:where(.hdk-controls)
|
|
108
|
+
:where(
|
|
109
|
+
input:not(
|
|
110
|
+
[type='checkbox'],
|
|
111
|
+
[type='radio'],
|
|
112
|
+
[type='range'],
|
|
113
|
+
[type='file'],
|
|
114
|
+
[type='color'],
|
|
115
|
+
[type='button'],
|
|
116
|
+
[type='submit'],
|
|
117
|
+
[type='reset'],
|
|
118
|
+
[type='image']
|
|
119
|
+
),
|
|
120
|
+
textarea,
|
|
121
|
+
select
|
|
122
|
+
) {
|
|
123
|
+
color: var(--color-text);
|
|
124
|
+
background-color: var(--color-bg-card);
|
|
125
|
+
border: 1px solid var(--color-border);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
:where(.hdk-controls)
|
|
129
|
+
:where(button, input[type='button'], input[type='submit'], input[type='reset']) {
|
|
130
|
+
/* inherit, not a token: an app that coloured a button's surface and not
|
|
131
|
+
* its text, or draws with currentColor inside it, keeps the colour it
|
|
132
|
+
* had from the page instead of jumping to the neutral tint's ink. */
|
|
133
|
+
color: inherit;
|
|
134
|
+
background-color: var(--color-neutral-bg);
|
|
135
|
+
border: 1px solid transparent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useReducedMotion Hook
|
|
3
|
+
* Detects if the user prefers reduced motion
|
|
4
|
+
* Returns true if animations should be disabled or simplified
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useEffect } from 'react'
|
|
8
|
+
|
|
9
|
+
export function useReducedMotion(): boolean {
|
|
10
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => {
|
|
11
|
+
// SSR and jsdom have no matchMedia: default to false.
|
|
12
|
+
if (typeof window === 'undefined' || !window.matchMedia) return false
|
|
13
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (typeof window === 'undefined' || !window.matchMedia) return
|
|
18
|
+
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)')
|
|
19
|
+
|
|
20
|
+
const handleChange = (event: MediaQueryListEvent) => {
|
|
21
|
+
setPrefersReducedMotion(event.matches)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Modern browsers
|
|
25
|
+
if (mediaQuery.addEventListener) {
|
|
26
|
+
mediaQuery.addEventListener('change', handleChange)
|
|
27
|
+
return () => mediaQuery.removeEventListener('change', handleChange)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fallback for older browsers
|
|
31
|
+
mediaQuery.addListener(handleChange)
|
|
32
|
+
return () => mediaQuery.removeListener(handleChange)
|
|
33
|
+
}, [])
|
|
34
|
+
|
|
35
|
+
return prefersReducedMotion
|
|
36
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useToast hook
|
|
3
|
+
* Manages toast notification state and display
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback } from 'react'
|
|
7
|
+
|
|
8
|
+
export interface ToastState {
|
|
9
|
+
id: number
|
|
10
|
+
message: string
|
|
11
|
+
type: 'success' | 'error' | 'info' | 'warning'
|
|
12
|
+
duration?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UseToastReturn {
|
|
16
|
+
toasts: ToastState[]
|
|
17
|
+
showToast: (
|
|
18
|
+
message: string,
|
|
19
|
+
type?: 'success' | 'error' | 'info' | 'warning',
|
|
20
|
+
duration?: number
|
|
21
|
+
) => void
|
|
22
|
+
dismissToast: (id: number) => void
|
|
23
|
+
clearAll: () => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let toastIdCounter = 0
|
|
27
|
+
|
|
28
|
+
export function useToast(): UseToastReturn {
|
|
29
|
+
const [toasts, setToasts] = useState<ToastState[]>([])
|
|
30
|
+
|
|
31
|
+
const showToast = useCallback(
|
|
32
|
+
(
|
|
33
|
+
message: string,
|
|
34
|
+
type: 'success' | 'error' | 'info' | 'warning' = 'info',
|
|
35
|
+
duration: number = 3000
|
|
36
|
+
) => {
|
|
37
|
+
const id = ++toastIdCounter
|
|
38
|
+
setToasts(prev => [...prev, { id, message, type, duration }])
|
|
39
|
+
},
|
|
40
|
+
[]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const dismissToast = useCallback((id: number) => {
|
|
44
|
+
setToasts(prev => prev.filter(toast => toast.id !== id))
|
|
45
|
+
}, [])
|
|
46
|
+
|
|
47
|
+
const clearAll = useCallback(() => {
|
|
48
|
+
setToasts([])
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
toasts,
|
|
53
|
+
showToast,
|
|
54
|
+
dismissToast,
|
|
55
|
+
clearAll
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/icon-btn.css
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* The round 32px icon button on the AppHeader bar — the theme picker trigger,
|
|
2
|
+
* the settings gear, and any link an app puts in the actions row.
|
|
3
|
+
*
|
|
4
|
+
* Defined ONCE. The picker and the gear each used to carry their own copy and
|
|
5
|
+
* the two had already drifted (padding 8px vs --hdk-space-sm, a ring on every
|
|
6
|
+
* mouse click vs on :focus-visible, a min-size on one only). `.theme-toggle-btn`
|
|
7
|
+
* and `.settings-toggle-btn` survive as hooks with no values of their own.
|
|
8
|
+
*
|
|
9
|
+
* Lives in its own file because ThemePicker renders outside the header too
|
|
10
|
+
* (HadokuThemePicker on a video overlay), so its trigger cannot depend on
|
|
11
|
+
* app-header.css. The chain is app-header.css -> theme-picker.css -> this.
|
|
12
|
+
*
|
|
13
|
+
* Works on an <a>: child apps ship without a preflight, so a bare anchor would
|
|
14
|
+
* render UA blue, visited purple and underlined. */
|
|
15
|
+
|
|
16
|
+
.app-header__icon-btn {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
width: 32px;
|
|
23
|
+
height: 32px;
|
|
24
|
+
min-width: 32px;
|
|
25
|
+
min-height: 32px;
|
|
26
|
+
padding: 0;
|
|
27
|
+
margin: 0;
|
|
28
|
+
border: none;
|
|
29
|
+
border-radius: 50%;
|
|
30
|
+
background: none;
|
|
31
|
+
color: var(--color-text);
|
|
32
|
+
font: inherit;
|
|
33
|
+
font-size: var(--hdk-text-lg);
|
|
34
|
+
line-height: 1;
|
|
35
|
+
text-decoration: none;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
opacity: 0.7;
|
|
38
|
+
transition:
|
|
39
|
+
background-color var(--transition-fast),
|
|
40
|
+
opacity var(--transition-fast);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Same colour visited or not, and no hover recolour from a host stylesheet's
|
|
44
|
+
* `a:hover` — the hover signal is the background below. */
|
|
45
|
+
.app-header__icon-btn:link,
|
|
46
|
+
.app-header__icon-btn:visited,
|
|
47
|
+
.app-header__icon-btn:hover,
|
|
48
|
+
.app-header__icon-btn:active {
|
|
49
|
+
color: var(--color-text);
|
|
50
|
+
text-decoration: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.app-header__icon-btn:hover {
|
|
54
|
+
background-color: var(--color-primary-hover);
|
|
55
|
+
opacity: 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* :focus-visible, not :focus — :focus rings on every mouse click. Full
|
|
59
|
+
* opacity too, so a keyboard user never lands on the dimmed resting state. */
|
|
60
|
+
.app-header__icon-btn:focus-visible {
|
|
61
|
+
outline: none;
|
|
62
|
+
opacity: 1;
|
|
63
|
+
box-shadow: 0 0 0 2px var(--color-primary);
|
|
64
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wolffm/task-ui-components
|
|
3
|
+
* Reusable UI components for task management applications
|
|
4
|
+
* Production-ready and fully typed
|
|
5
|
+
*
|
|
6
|
+
* 2.2.0 — AppHeader + ConnectedSettings (with a `children` extension slot).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Main components
|
|
10
|
+
//
|
|
11
|
+
// NOTE: `ThemePicker` and `ConnectedThemePicker` are intentionally NOT exported.
|
|
12
|
+
// AppHeader renders the theme picker itself from the HadokuThemeRoot context,
|
|
13
|
+
// so every app gets the same control by construction. Exporting them is what
|
|
14
|
+
// let an app hand-roll its own (with its own open-state and a debug logger in
|
|
15
|
+
// the icon resolver) — a compile error is the only enforcement that actually
|
|
16
|
+
// holds. If you need theme state, use `useTheme()` from @wolffm/themes.
|
|
17
|
+
// The picker itself, for an app with no header row to put it in (watchpart2's
|
|
18
|
+
// UI is a video overlay). It takes NO props — placement is free, configuration
|
|
19
|
+
// is not, which is the property that keeps it identical everywhere.
|
|
20
|
+
export { HadokuThemePicker } from './components/HadokuThemePicker'
|
|
21
|
+
export { AppHeader } from './components/AppHeader'
|
|
22
|
+
export { ConnectedSettings } from './components/ConnectedSettings'
|
|
23
|
+
// Re-exported from @wolffm/themes, which now owns the context AND the provider
|
|
24
|
+
// that fills it. These are RE-EXPORTS, not re-declarations: every consumer gets
|
|
25
|
+
// the one context object from themes, which is what makes a duplicated copy of
|
|
26
|
+
// THIS package harmless. Kept here so existing
|
|
27
|
+
// `import { useHadokuTheme } from '@wolffm/task-ui-components'` call sites
|
|
28
|
+
// across the fleet keep working unchanged.
|
|
29
|
+
export { HadokuThemeProvider, useHadokuTheme, useHadokuThemeOptional } from '@wolffm/themes'
|
|
30
|
+
export type { HadokuThemeValue, HadokuThemeProviderProps } from '@wolffm/themes'
|
|
31
|
+
export { Toast } from './components/Toast'
|
|
32
|
+
export { Toaster } from './components/Toaster'
|
|
33
|
+
export { Modal } from './components/Modal'
|
|
34
|
+
export { ContextMenu } from './components/ContextMenu'
|
|
35
|
+
export { LoadingSkeleton } from './components/LoadingSkeleton'
|
|
36
|
+
export { SettingsModal } from './components/SettingsModal'
|
|
37
|
+
// Themed replacements for the browser's default controls. Scrollbars are
|
|
38
|
+
// CSS only: import '@wolffm/task-ui-components/scrollbar.css' and add
|
|
39
|
+
// `hdk-scroll` to the app root.
|
|
40
|
+
export { Button } from './components/Button'
|
|
41
|
+
export { Select, NativeSelect } from './components/Select'
|
|
42
|
+
export { NumberInput } from './components/NumberInput'
|
|
43
|
+
export { Tooltip } from './components/Tooltip'
|
|
44
|
+
// Framework-free; also its own entry, `@wolffm/task-ui-components/title-tooltips`,
|
|
45
|
+
// so the site shell can install it without React.
|
|
46
|
+
export { installTitleTooltips } from './lib/titleTooltips'
|
|
47
|
+
export type { TitleTooltipOptions } from './lib/titleTooltips'
|
|
48
|
+
// Same, `@wolffm/task-ui-components/select-menus`: the themed list for every
|
|
49
|
+
// native <select>.
|
|
50
|
+
export { installSelectMenus } from './lib/selectMenus'
|
|
51
|
+
|
|
52
|
+
// Bento components
|
|
53
|
+
export { BentoGrid } from './components/BentoGrid'
|
|
54
|
+
export { BentoCard } from './components/BentoCard'
|
|
55
|
+
|
|
56
|
+
// Hooks
|
|
57
|
+
export { useToast } from './hooks/useToast'
|
|
58
|
+
export { useReducedMotion } from './hooks/useReducedMotion'
|
|
59
|
+
|
|
60
|
+
// Theme icons - export individually for flexibility
|
|
61
|
+
export {
|
|
62
|
+
SettingsIcon,
|
|
63
|
+
TagIcon,
|
|
64
|
+
// Generic/Fallback icons
|
|
65
|
+
CircleIcon,
|
|
66
|
+
SquareIcon,
|
|
67
|
+
TriangleIcon,
|
|
68
|
+
DiamondIcon,
|
|
69
|
+
StarIcon,
|
|
70
|
+
HexagonIcon,
|
|
71
|
+
PentagonIcon,
|
|
72
|
+
OctagonIcon,
|
|
73
|
+
FALLBACK_ICONS,
|
|
74
|
+
getFallbackIcon
|
|
75
|
+
} from './components/ThemeIcons'
|
|
76
|
+
// Theme-family icons moved to @wolffm/themes with the rest of the theme model.
|
|
77
|
+
// Re-exported so existing imports from this package keep resolving.
|
|
78
|
+
export {
|
|
79
|
+
SunIcon,
|
|
80
|
+
MoonIcon,
|
|
81
|
+
StrawberryIcon,
|
|
82
|
+
WaveIcon,
|
|
83
|
+
ZapIcon,
|
|
84
|
+
CoffeeIcon,
|
|
85
|
+
FlowerIcon,
|
|
86
|
+
HeartIcon,
|
|
87
|
+
LeafIcon,
|
|
88
|
+
LanternIcon
|
|
89
|
+
} from '@wolffm/themes'
|
|
90
|
+
|
|
91
|
+
// Types
|
|
92
|
+
// ThemeFamily stays — @wolffm/themes builds THEME_FAMILIES against it.
|
|
93
|
+
// ThemePickerProps / ConnectedThemePickerProps are gone with the components:
|
|
94
|
+
// a prop type you cannot render is only useful for reimplementing the control.
|
|
95
|
+
export type { ThemeName, ThemeFamily, DropdownPlacement, ThemeMode } from './types'
|
|
96
|
+
export type { AppHeaderProps } from './components/AppHeader'
|
|
97
|
+
export type { ConnectedSettingsProps } from './components/ConnectedSettings'
|
|
98
|
+
export type {
|
|
99
|
+
ContentLevelState,
|
|
100
|
+
Identity,
|
|
101
|
+
SignIn,
|
|
102
|
+
SignInClient,
|
|
103
|
+
SignInResult,
|
|
104
|
+
Tier
|
|
105
|
+
} from './lib/settingsClient'
|
|
106
|
+
export {
|
|
107
|
+
loadSignIn,
|
|
108
|
+
loadSignInClient,
|
|
109
|
+
passkeyHref,
|
|
110
|
+
passkeysSupported,
|
|
111
|
+
switchKeyHref
|
|
112
|
+
} from './lib/settingsClient'
|
|
113
|
+
export type { ToastProps } from './components/Toast'
|
|
114
|
+
export type { ToasterProps, ToastState } from './components/Toaster'
|
|
115
|
+
export type { UseToastReturn } from './hooks/useToast'
|
|
116
|
+
export type { ModalProps } from './components/Modal'
|
|
117
|
+
export type { ContextMenuProps, ContextMenuItem } from './components/ContextMenu'
|
|
118
|
+
export type { LoadingSkeletonProps } from './components/LoadingSkeleton'
|
|
119
|
+
export type {
|
|
120
|
+
ButtonProps,
|
|
121
|
+
ButtonVariant,
|
|
122
|
+
ButtonFamily,
|
|
123
|
+
ButtonSize,
|
|
124
|
+
ButtonShape
|
|
125
|
+
} from './components/Button'
|
|
126
|
+
export type {
|
|
127
|
+
SelectProps,
|
|
128
|
+
SelectOption,
|
|
129
|
+
SelectVariant,
|
|
130
|
+
SelectSize,
|
|
131
|
+
NativeSelectProps
|
|
132
|
+
} from './components/Select'
|
|
133
|
+
export type { TooltipProps, TooltipPlacement, TooltipVariant } from './components/Tooltip'
|
|
134
|
+
export type {
|
|
135
|
+
SettingsModalProps,
|
|
136
|
+
SettingsSection,
|
|
137
|
+
SettingsField,
|
|
138
|
+
SettingsToggleField,
|
|
139
|
+
SettingsTextInputField,
|
|
140
|
+
SettingsPasswordField,
|
|
141
|
+
SettingsSelectField,
|
|
142
|
+
SettingsButtonField,
|
|
143
|
+
SettingsCustomField
|
|
144
|
+
} from './components/SettingsModal'
|
|
145
|
+
export type { BentoGridProps, BentoColumns } from './components/BentoGrid'
|
|
146
|
+
export type { BentoCardProps, CardSize } from './components/BentoCard'
|
|
147
|
+
|
|
148
|
+
// `logger` was removed in v2.0.0. Migrate to `@wolffm/logger/client`:
|
|
149
|
+
// - import { logger } from '@wolffm/task-ui-components'
|
|
150
|
+
// + import { logger } from '@wolffm/logger/client'
|
|
151
|
+
// See packages/logger in hadoku_site for the consolidated cross-runtime
|
|
152
|
+
// implementation. UI helpers (theme, component, preference, apiRequest,
|
|
153
|
+
// apiResponse, fallback) are preserved on the new client logger.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a dropdown list goes under (or over) its trigger, shared by <Select>
|
|
3
|
+
* and installSelectMenus so both lists sit and flip identically.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const MENU_GAP = 4
|
|
7
|
+
export const MENU_MAX_HEIGHT = 280
|
|
8
|
+
|
|
9
|
+
export interface MenuPosition {
|
|
10
|
+
left: number
|
|
11
|
+
width: number
|
|
12
|
+
top?: number
|
|
13
|
+
bottom?: number
|
|
14
|
+
maxHeight: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** `listHeight` is the list's full scrollHeight, so a short list never flips. */
|
|
18
|
+
export function placeMenu(rect: DOMRect, listHeight: number): MenuPosition {
|
|
19
|
+
const below = window.innerHeight - rect.bottom - MENU_GAP
|
|
20
|
+
const above = rect.top - MENU_GAP
|
|
21
|
+
// Flip up only when the list does not fit below AND there is more room above.
|
|
22
|
+
const flip = below < Math.min(MENU_MAX_HEIGHT, listHeight) && above > below
|
|
23
|
+
return {
|
|
24
|
+
left: rect.left,
|
|
25
|
+
width: rect.width,
|
|
26
|
+
...(flip
|
|
27
|
+
? { bottom: window.innerHeight - rect.top + MENU_GAP }
|
|
28
|
+
: { top: rect.bottom + MENU_GAP }),
|
|
29
|
+
maxHeight: Math.max(120, Math.min(MENU_MAX_HEIGHT, flip ? above : below) - 8)
|
|
30
|
+
}
|
|
31
|
+
}
|