@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
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/* AppHeader + ConnectedSettings styles for @wolffm/task-ui-components.
|
|
2
|
+
*
|
|
3
|
+
* All colors come from @wolffm/themes semantic tokens so the header themes with
|
|
4
|
+
* everything else. One import is all an app needs:
|
|
5
|
+
* import '@wolffm/task-ui-components/app-header.css'
|
|
6
|
+
*
|
|
7
|
+
* This used to say "import alongside theme-picker.css", and that convention
|
|
8
|
+
* failed exactly the way conventions do. AppHeader renders the theme picker
|
|
9
|
+
* unconditionally now — it is not a prop — so an app that loads this file
|
|
10
|
+
* without the picker's gets the right component with none of its styling. The
|
|
11
|
+
* command station shipped like that after its old wrapper (the only importer of
|
|
12
|
+
* theme-picker.css on that site) was deleted: correct markup, no CSS.
|
|
13
|
+
*
|
|
14
|
+
* Pulling it in here makes the pair inseparable, which is the same reason the
|
|
15
|
+
* component itself stopped being a prop. */
|
|
16
|
+
@import './theme-picker.css';
|
|
17
|
+
|
|
18
|
+
/* ── Header layout ─────────────────────────────────────────────────────────
|
|
19
|
+
* Title left, action bar right, vertically centered. Full width of whatever
|
|
20
|
+
* column the app places it in. */
|
|
21
|
+
.app-header {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: space-between;
|
|
25
|
+
gap: 1rem;
|
|
26
|
+
padding-bottom: 1rem;
|
|
27
|
+
margin-bottom: 1.5rem;
|
|
28
|
+
border-bottom: 1px solid var(--color-border);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Body text, NOT --color-primary. `primary` is a fill colour: sanctioned only
|
|
32
|
+
* under --color-on-primary. As bare text on --color-bg it clears the 3:1
|
|
33
|
+
* large-text floor in just 12 of 18 themes (izakaya-light 1.92, nature-light
|
|
34
|
+
* 2.15, ocean-light 2.28, lavender-light 2.53, pink-light 2.56,
|
|
35
|
+
* strawberry-light 2.61). --color-text clears it everywhere at 8.87–17.58.
|
|
36
|
+
* The brand accent lives in the border-bottom instead. */
|
|
37
|
+
.app-header__title {
|
|
38
|
+
margin: 0;
|
|
39
|
+
color: var(--color-text);
|
|
40
|
+
font-size: var(--hdk-text-lg);
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
line-height: 1.2;
|
|
43
|
+
/* A flex item floors at its min-content width, so without `min-width: 0` a
|
|
44
|
+
* long title pushed the actions off a 320px screen instead of giving way. */
|
|
45
|
+
min-width: 0;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
text-overflow: ellipsis;
|
|
48
|
+
white-space: nowrap;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Never shrinks: the controls keep their 32px hit areas and the title is what
|
|
52
|
+
* gives way. 0.5rem is enough between icons — the round hit areas already
|
|
53
|
+
* space them visually. */
|
|
54
|
+
.app-header__actions {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
gap: 0.5rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* In the header the theme picker sits directly left of the settings gear, and
|
|
62
|
+
* the base ThemePicker opens its dropdown SIDEWAYS (which suits a vertical /
|
|
63
|
+
* sidebar placement). Here that would cover the gear — so scope it to drop
|
|
64
|
+
* straight DOWN, right-aligned, opening into the header away from the gear. */
|
|
65
|
+
.app-header .theme-picker__dropdown,
|
|
66
|
+
.app-header .theme-picker--open-left .theme-picker__dropdown {
|
|
67
|
+
top: calc(100% + 8px);
|
|
68
|
+
left: auto;
|
|
69
|
+
right: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ── ConnectedSettings: gear toggle ────────────────────────────────────────
|
|
73
|
+
* The gear itself is a .app-header__icon-btn (icon-btn.css, via
|
|
74
|
+
* theme-picker.css), the same rules as the picker trigger beside it. */
|
|
75
|
+
.settings-popout {
|
|
76
|
+
position: relative;
|
|
77
|
+
display: inline-flex;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Full-viewport click-catcher, same trick ThemePicker uses. */
|
|
81
|
+
.settings-popout__overlay {
|
|
82
|
+
position: fixed;
|
|
83
|
+
inset: 0;
|
|
84
|
+
z-index: 99;
|
|
85
|
+
background: transparent;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.settings-popout__panel {
|
|
89
|
+
position: absolute;
|
|
90
|
+
top: calc(100% + 8px);
|
|
91
|
+
right: 0;
|
|
92
|
+
z-index: 100;
|
|
93
|
+
/* These apps ship without a Tailwind preflight, so box-sizing is content-box
|
|
94
|
+
* unless a rule says otherwise: without this, `width: 300px` renders 334px
|
|
95
|
+
* (+32 padding +2 border) and `max-width` clamps the CONTENT box, leaving the
|
|
96
|
+
* popout 2px wider than a 320px viewport. */
|
|
97
|
+
box-sizing: border-box;
|
|
98
|
+
width: 300px;
|
|
99
|
+
max-width: calc(100vw - 32px);
|
|
100
|
+
/* Cap height so an app that adds its own preferences section stays on screen;
|
|
101
|
+
* the popout scrolls rather than overflowing the viewport. */
|
|
102
|
+
max-height: calc(100vh - 80px);
|
|
103
|
+
overflow-y: auto;
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
gap: 14px;
|
|
107
|
+
padding: 16px;
|
|
108
|
+
border-radius: 12px;
|
|
109
|
+
background: var(--color-bg-card);
|
|
110
|
+
border: 1px solid var(--color-border);
|
|
111
|
+
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
|
|
112
|
+
color: var(--color-text);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Divider above the optional app-specific settings section. */
|
|
116
|
+
.settings-popout__app {
|
|
117
|
+
border-top: 1px solid var(--color-border);
|
|
118
|
+
padding-top: 12px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.settings-popout__header {
|
|
122
|
+
font-weight: 600;
|
|
123
|
+
font-size: 0.95rem;
|
|
124
|
+
color: var(--color-text);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.settings-popout__row {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
justify-content: space-between;
|
|
131
|
+
gap: 10px;
|
|
132
|
+
}
|
|
133
|
+
.settings-popout__row--stack {
|
|
134
|
+
flex-direction: column;
|
|
135
|
+
align-items: stretch;
|
|
136
|
+
gap: 8px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.settings-popout__label {
|
|
140
|
+
font-size: 0.8rem;
|
|
141
|
+
color: var(--color-text-secondary);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.settings-popout__value {
|
|
145
|
+
font-size: 0.85rem;
|
|
146
|
+
font-weight: 500;
|
|
147
|
+
color: var(--color-text);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Label + control pair on the right of a row. `min-width: 0` is load-bearing:
|
|
151
|
+
* as a flex item of __row it otherwise floors at its own min-content width, and
|
|
152
|
+
* that includes the input's default `size=20` intrinsic width (195px in Chrome).
|
|
153
|
+
* Pair + gap + Save came to 251.4px against a 300px row that also has to hold
|
|
154
|
+
* the label — so the name row overflowed the panel the moment Edit was clicked,
|
|
155
|
+
* and __panel's `overflow-y: auto` computes overflow-x to `auto`, turning that
|
|
156
|
+
* into a horizontal scrollbar rather than a clip. */
|
|
157
|
+
.settings-popout__inline {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: flex-end;
|
|
161
|
+
gap: 6px;
|
|
162
|
+
/* Claim the row's free space rather than shrink-wrapping, so the input below
|
|
163
|
+
* has something to grow into now that its basis is 0. */
|
|
164
|
+
flex: 1 1 auto;
|
|
165
|
+
min-width: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.settings-popout__tier {
|
|
169
|
+
font-size: 0.78rem;
|
|
170
|
+
font-weight: 600;
|
|
171
|
+
padding: 2px 10px;
|
|
172
|
+
border-radius: 999px;
|
|
173
|
+
background: var(--color-bg-alt);
|
|
174
|
+
color: var(--color-text);
|
|
175
|
+
border: 1px solid var(--color-border);
|
|
176
|
+
}
|
|
177
|
+
.settings-popout__tier--admin {
|
|
178
|
+
background: var(--color-primary-bg);
|
|
179
|
+
color: var(--color-on-primary-bg);
|
|
180
|
+
border-color: var(--color-primary);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.settings-popout__input {
|
|
184
|
+
/* `1 1 0` + `width: 0`, not `1 1 auto`: the basis must not come from the
|
|
185
|
+
* input's `size`-derived intrinsic width, or the row cannot shrink below it. */
|
|
186
|
+
flex: 1 1 0;
|
|
187
|
+
width: 0;
|
|
188
|
+
min-width: 0;
|
|
189
|
+
box-sizing: border-box;
|
|
190
|
+
font-size: 0.85rem;
|
|
191
|
+
padding: 5px 8px;
|
|
192
|
+
border-radius: 7px;
|
|
193
|
+
border: 1px solid var(--color-border);
|
|
194
|
+
background: var(--color-bg);
|
|
195
|
+
color: var(--color-text);
|
|
196
|
+
}
|
|
197
|
+
.settings-popout__input:focus-visible {
|
|
198
|
+
outline: none;
|
|
199
|
+
border-color: var(--color-primary);
|
|
200
|
+
box-shadow: 0 0 0 2px var(--color-primary-bg);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.settings-popout__btn {
|
|
204
|
+
font-size: 0.78rem;
|
|
205
|
+
padding: 4px 10px;
|
|
206
|
+
border-radius: 7px;
|
|
207
|
+
border: 1px solid var(--color-border);
|
|
208
|
+
background: var(--color-bg);
|
|
209
|
+
color: var(--color-text);
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
white-space: nowrap;
|
|
212
|
+
text-decoration: none;
|
|
213
|
+
transition:
|
|
214
|
+
background-color 0.15s ease,
|
|
215
|
+
border-color 0.15s ease;
|
|
216
|
+
}
|
|
217
|
+
.settings-popout__btn:hover:not(:disabled) {
|
|
218
|
+
background: var(--color-bg-alt);
|
|
219
|
+
}
|
|
220
|
+
.settings-popout__btn--primary {
|
|
221
|
+
background: var(--color-primary);
|
|
222
|
+
border-color: var(--color-primary);
|
|
223
|
+
color: var(--color-on-primary);
|
|
224
|
+
}
|
|
225
|
+
.settings-popout__btn--primary:hover:not(:disabled) {
|
|
226
|
+
background: var(--color-primary-dark);
|
|
227
|
+
}
|
|
228
|
+
.settings-popout__btn:disabled {
|
|
229
|
+
opacity: 0.6;
|
|
230
|
+
cursor: default;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Content pill — a chunked "volume slider": segments 1..maxLevel, everything
|
|
234
|
+
* up to and including the selected level reads as filled. */
|
|
235
|
+
.settings-popout__pill {
|
|
236
|
+
display: flex;
|
|
237
|
+
gap: 3px;
|
|
238
|
+
width: 100%;
|
|
239
|
+
}
|
|
240
|
+
.settings-popout__seg {
|
|
241
|
+
flex: 1 1 0;
|
|
242
|
+
padding: 7px 0;
|
|
243
|
+
font-size: 0.8rem;
|
|
244
|
+
font-weight: 600;
|
|
245
|
+
border: 1px solid var(--color-border);
|
|
246
|
+
background: var(--color-bg-alt);
|
|
247
|
+
color: var(--color-text-secondary);
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
transition:
|
|
250
|
+
background-color 0.12s ease,
|
|
251
|
+
color 0.12s ease;
|
|
252
|
+
}
|
|
253
|
+
.settings-popout__seg:first-child {
|
|
254
|
+
border-radius: 8px 0 0 8px;
|
|
255
|
+
}
|
|
256
|
+
.settings-popout__seg:last-child {
|
|
257
|
+
border-radius: 0 8px 8px 0;
|
|
258
|
+
}
|
|
259
|
+
.settings-popout__seg--filled {
|
|
260
|
+
background: var(--color-primary);
|
|
261
|
+
border-color: var(--color-primary);
|
|
262
|
+
color: var(--color-on-primary);
|
|
263
|
+
}
|
|
264
|
+
.settings-popout__seg:disabled {
|
|
265
|
+
cursor: default;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Inline sign-in: label above, input + submit on one line, error below. */
|
|
269
|
+
.settings-popout__key-form {
|
|
270
|
+
display: flex;
|
|
271
|
+
flex-direction: column;
|
|
272
|
+
gap: 6px;
|
|
273
|
+
width: 100%;
|
|
274
|
+
}
|
|
275
|
+
.settings-popout__error {
|
|
276
|
+
font-size: 0.75rem;
|
|
277
|
+
line-height: 1.35;
|
|
278
|
+
padding: 4px 8px;
|
|
279
|
+
border-radius: 6px;
|
|
280
|
+
background: var(--color-danger-bg);
|
|
281
|
+
color: var(--color-on-danger-bg);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.settings-popout__hint {
|
|
285
|
+
font-size: 0.72rem;
|
|
286
|
+
line-height: 1.35;
|
|
287
|
+
color: var(--color-text-tertiary);
|
|
288
|
+
}
|
package/dist/bento.css
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bento Component Styles
|
|
3
|
+
* Styles for BentoGrid and BentoCard
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* ===== BENTO GRID ===== */
|
|
7
|
+
.bento-grid {
|
|
8
|
+
display: grid;
|
|
9
|
+
gap: var(--bento-gap, 1rem);
|
|
10
|
+
padding: 1rem;
|
|
11
|
+
width: 100%;
|
|
12
|
+
max-width: var(--bento-max-width, 1400px);
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
|
|
15
|
+
/* Mobile first: single column */
|
|
16
|
+
grid-template-columns: 1fr;
|
|
17
|
+
grid-auto-rows: minmax(120px, auto);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Small tablets: 2 columns */
|
|
21
|
+
@media (min-width: 640px) {
|
|
22
|
+
.bento-grid {
|
|
23
|
+
grid-template-columns: repeat(var(--bento-cols-sm, 2), 1fr);
|
|
24
|
+
gap: calc(var(--bento-gap, 1rem) * 1.25);
|
|
25
|
+
padding: 1.5rem;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Tablets: 3 columns */
|
|
30
|
+
@media (min-width: 768px) {
|
|
31
|
+
.bento-grid {
|
|
32
|
+
grid-template-columns: repeat(var(--bento-cols-md, 3), 1fr);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Desktop: 4 columns */
|
|
37
|
+
@media (min-width: 1024px) {
|
|
38
|
+
.bento-grid {
|
|
39
|
+
grid-template-columns: repeat(var(--bento-cols-lg, 4), 1fr);
|
|
40
|
+
gap: calc(var(--bento-gap, 1rem) * 1.5);
|
|
41
|
+
padding: 2rem;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Large desktop: 6 columns */
|
|
46
|
+
@media (min-width: 1400px) {
|
|
47
|
+
.bento-grid {
|
|
48
|
+
grid-template-columns: repeat(var(--bento-cols-xl, 6), 1fr);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ===== BENTO CARD ===== */
|
|
53
|
+
.bento-card {
|
|
54
|
+
position: relative;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
border-radius: var(--hdk-radius-lg);
|
|
57
|
+
background: var(--color-bg-card);
|
|
58
|
+
border: 1px solid var(--color-border-light);
|
|
59
|
+
box-shadow: var(--hdk-shadow-md);
|
|
60
|
+
transition:
|
|
61
|
+
transform var(--transition-fast, 0.15s) ease,
|
|
62
|
+
box-shadow var(--transition-fast, 0.15s) ease;
|
|
63
|
+
|
|
64
|
+
/* GPU acceleration for smooth animations */
|
|
65
|
+
transform: translateZ(0);
|
|
66
|
+
backface-visibility: hidden;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.bento-card[role='button'] {
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.bento-card[role='button']:hover:not(.bento-card--disabled) {
|
|
74
|
+
transform: scale(1.02) translateZ(0);
|
|
75
|
+
box-shadow: var(--hdk-shadow-lg);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.bento-card[role='button']:active:not(.bento-card--disabled) {
|
|
79
|
+
transform: scale(0.98) translateZ(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.bento-card:focus-visible {
|
|
83
|
+
outline: none;
|
|
84
|
+
box-shadow: var(--hdk-shadow-focus), var(--hdk-shadow-md);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Card Sizes */
|
|
88
|
+
.bento-card--default {
|
|
89
|
+
grid-column: span 1;
|
|
90
|
+
grid-row: span 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.bento-card--wide {
|
|
94
|
+
grid-column: span 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@media (min-width: 640px) {
|
|
98
|
+
.bento-card--wide {
|
|
99
|
+
grid-column: span 2;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.bento-card--tall {
|
|
104
|
+
grid-row: span 2;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.bento-card--large {
|
|
108
|
+
grid-column: span 1;
|
|
109
|
+
grid-row: span 1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@media (min-width: 640px) {
|
|
113
|
+
.bento-card--large {
|
|
114
|
+
grid-column: span 2;
|
|
115
|
+
grid-row: span 2;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.bento-card--hero {
|
|
120
|
+
grid-column: 1 / -1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@media (min-width: 768px) {
|
|
124
|
+
.bento-card--hero {
|
|
125
|
+
grid-column: span 3;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Disabled State (preloading) */
|
|
130
|
+
.bento-card--disabled {
|
|
131
|
+
pointer-events: none;
|
|
132
|
+
opacity: 0.85;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Respect reduced motion preference */
|
|
136
|
+
@media (prefers-reduced-motion: reduce) {
|
|
137
|
+
.bento-card,
|
|
138
|
+
.bento-card[role='button']:hover:not(.bento-card--disabled),
|
|
139
|
+
.bento-card[role='button']:active:not(.bento-card--disabled) {
|
|
140
|
+
transform: none;
|
|
141
|
+
transition: none;
|
|
142
|
+
}
|
|
143
|
+
}
|
package/dist/button.css
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/* Button styles for @wolffm/task-ui-components.
|
|
2
|
+
*
|
|
3
|
+
* import '@wolffm/task-ui-components/button.css'
|
|
4
|
+
*
|
|
5
|
+
* Each family modifier maps its six theme tokens onto private `--_btn-*`
|
|
6
|
+
* properties, and each variant paints from those. That keeps the matrix at
|
|
7
|
+
* 5 + 5 rules instead of 25, and every pairing below is a sanctioned one:
|
|
8
|
+
* `on-<f>` over `<f>`, `on-<f>-bg` over `<f>-bg` (THEME_USAGE_GUIDE.md).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.hdk-btn {
|
|
12
|
+
--_btn-c: var(--color-primary);
|
|
13
|
+
--_btn-c-dark: var(--color-primary-dark);
|
|
14
|
+
--_btn-on: var(--color-on-primary);
|
|
15
|
+
--_btn-tint: var(--color-primary-bg);
|
|
16
|
+
--_btn-on-tint: var(--color-on-primary-bg);
|
|
17
|
+
--_btn-hover: var(--color-primary-hover);
|
|
18
|
+
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
gap: 6px;
|
|
23
|
+
font: inherit;
|
|
24
|
+
font-weight: var(--font-weight-semibold);
|
|
25
|
+
line-height: 1.2;
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
border: 1px solid transparent;
|
|
28
|
+
border-radius: var(--hdk-radius);
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
user-select: none;
|
|
31
|
+
transition:
|
|
32
|
+
background-color var(--transition-fast),
|
|
33
|
+
border-color var(--transition-fast),
|
|
34
|
+
color var(--transition-fast),
|
|
35
|
+
box-shadow var(--transition-fast),
|
|
36
|
+
transform var(--transition-fast);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.hdk-btn:focus-visible {
|
|
40
|
+
outline: none;
|
|
41
|
+
box-shadow: var(--hdk-shadow-focus);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.hdk-btn:active:not(:disabled) {
|
|
45
|
+
transform: translateY(1px);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.hdk-btn:disabled {
|
|
49
|
+
opacity: 0.5;
|
|
50
|
+
cursor: not-allowed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* A loading button is disabled for clicks but is not "unavailable", so it
|
|
54
|
+
* keeps full opacity — the spinner is the signal. */
|
|
55
|
+
.hdk-btn--loading:disabled {
|
|
56
|
+
opacity: 1;
|
|
57
|
+
cursor: progress;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ── families ─────────────────────────────────────────────────────────── */
|
|
61
|
+
|
|
62
|
+
.hdk-btn--success {
|
|
63
|
+
--_btn-c: var(--color-success);
|
|
64
|
+
--_btn-c-dark: var(--color-success-dark);
|
|
65
|
+
--_btn-on: var(--color-on-success);
|
|
66
|
+
--_btn-tint: var(--color-success-bg);
|
|
67
|
+
--_btn-on-tint: var(--color-on-success-bg);
|
|
68
|
+
--_btn-hover: var(--color-success-hover);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.hdk-btn--warning {
|
|
72
|
+
--_btn-c: var(--color-warning);
|
|
73
|
+
--_btn-c-dark: var(--color-warning-dark);
|
|
74
|
+
--_btn-on: var(--color-on-warning);
|
|
75
|
+
--_btn-tint: var(--color-warning-bg);
|
|
76
|
+
--_btn-on-tint: var(--color-on-warning-bg);
|
|
77
|
+
--_btn-hover: var(--color-warning-hover);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.hdk-btn--danger {
|
|
81
|
+
--_btn-c: var(--color-danger);
|
|
82
|
+
--_btn-c-dark: var(--color-danger-dark);
|
|
83
|
+
--_btn-on: var(--color-on-danger);
|
|
84
|
+
--_btn-tint: var(--color-danger-bg);
|
|
85
|
+
--_btn-on-tint: var(--color-on-danger-bg);
|
|
86
|
+
--_btn-hover: var(--color-danger-hover);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.hdk-btn--neutral {
|
|
90
|
+
--_btn-c: var(--color-neutral);
|
|
91
|
+
--_btn-c-dark: var(--color-neutral-dark);
|
|
92
|
+
--_btn-on: var(--color-on-neutral);
|
|
93
|
+
--_btn-tint: var(--color-neutral-bg);
|
|
94
|
+
--_btn-on-tint: var(--color-on-neutral-bg);
|
|
95
|
+
--_btn-hover: var(--color-neutral-hover);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ── variants ─────────────────────────────────────────────────────────── */
|
|
99
|
+
|
|
100
|
+
.hdk-btn--solid {
|
|
101
|
+
background-color: var(--_btn-c);
|
|
102
|
+
color: var(--_btn-on);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.hdk-btn--solid:hover:not(:disabled) {
|
|
106
|
+
background-color: var(--_btn-c-dark);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* The task app's own `.pill-btn--active` look, as a reusable treatment. */
|
|
110
|
+
.hdk-btn--gradient {
|
|
111
|
+
background: linear-gradient(180deg, var(--_btn-c) 0%, var(--_btn-c-dark) 100%);
|
|
112
|
+
color: var(--_btn-on);
|
|
113
|
+
box-shadow: var(--hdk-shadow-sm);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.hdk-btn--gradient:hover:not(:disabled) {
|
|
117
|
+
box-shadow: var(--hdk-shadow-md);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.hdk-btn--soft {
|
|
121
|
+
background-color: var(--_btn-tint);
|
|
122
|
+
color: var(--_btn-on-tint);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.hdk-btn--soft:hover:not(:disabled) {
|
|
126
|
+
border-color: var(--_btn-c);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.hdk-btn--outline {
|
|
130
|
+
background-color: transparent;
|
|
131
|
+
border-color: var(--_btn-c);
|
|
132
|
+
color: var(--color-text);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.hdk-btn--outline:hover:not(:disabled) {
|
|
136
|
+
background-color: var(--_btn-tint);
|
|
137
|
+
color: var(--_btn-on-tint);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.hdk-btn--ghost {
|
|
141
|
+
background-color: transparent;
|
|
142
|
+
color: var(--color-text-secondary);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.hdk-btn--ghost:hover:not(:disabled) {
|
|
146
|
+
background-color: var(--_btn-tint);
|
|
147
|
+
color: var(--_btn-on-tint);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ── sizes ────────────────────────────────────────────────────────────── */
|
|
151
|
+
|
|
152
|
+
.hdk-btn--sm {
|
|
153
|
+
font-size: var(--hdk-text-xs);
|
|
154
|
+
padding: 4px 10px;
|
|
155
|
+
min-height: 28px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.hdk-btn--md {
|
|
159
|
+
font-size: var(--hdk-text-sm);
|
|
160
|
+
padding: 7px 14px;
|
|
161
|
+
min-height: 34px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.hdk-btn--lg {
|
|
165
|
+
font-size: var(--hdk-text-base);
|
|
166
|
+
padding: 10px 18px;
|
|
167
|
+
min-height: 42px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.hdk-btn--icon-only {
|
|
171
|
+
padding: 0;
|
|
172
|
+
aspect-ratio: 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.hdk-btn--icon-only.hdk-btn--sm {
|
|
176
|
+
width: 28px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.hdk-btn--icon-only.hdk-btn--md {
|
|
180
|
+
width: 34px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.hdk-btn--icon-only.hdk-btn--lg {
|
|
184
|
+
width: 42px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* ── shapes ───────────────────────────────────────────────────────────── */
|
|
188
|
+
|
|
189
|
+
.hdk-btn--pill {
|
|
190
|
+
border-radius: 999px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.hdk-btn--square {
|
|
194
|
+
border-radius: var(--hdk-radius-sm);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.hdk-btn--block {
|
|
198
|
+
display: flex;
|
|
199
|
+
width: 100%;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* ── loading ──────────────────────────────────────────────────────────── */
|
|
203
|
+
|
|
204
|
+
.hdk-btn__spinner {
|
|
205
|
+
width: 1em;
|
|
206
|
+
height: 1em;
|
|
207
|
+
border: 2px solid currentColor;
|
|
208
|
+
border-right-color: transparent;
|
|
209
|
+
border-radius: 50%;
|
|
210
|
+
animation: hdk-btn-spin 0.7s linear infinite;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@keyframes hdk-btn-spin {
|
|
214
|
+
to {
|
|
215
|
+
transform: rotate(360deg);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
@media (prefers-reduced-motion: reduce) {
|
|
220
|
+
.hdk-btn__spinner {
|
|
221
|
+
animation-duration: 2s;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.hdk-btn:active:not(:disabled) {
|
|
225
|
+
transform: none;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 { type ConnectedSettingsProps } from './ConnectedSettings';
|
|
40
|
+
export interface AppHeaderProps {
|
|
41
|
+
/** Left-aligned header title. A string is wrapped in an <h1>; pass a node for
|
|
42
|
+
* custom markup (e.g. a clickable title button). */
|
|
43
|
+
title: React.ReactNode;
|
|
44
|
+
/** Optional status/extra node shown left of the theme + settings controls
|
|
45
|
+
* (e.g. an "API Online" indicator). Acceptable per the header contract. */
|
|
46
|
+
status?: React.ReactNode;
|
|
47
|
+
/** Identity hints for the settings popout. Narrow on purpose: `userType` and
|
|
48
|
+
* `name` exist because an app behind a path-prefixed shim may not be able to
|
|
49
|
+
* reach `/session/whoami` (conjure), NOT as a styling or layout hook. */
|
|
50
|
+
settingsProps?: ConnectedSettingsProps;
|
|
51
|
+
/** THE app-specific extension point. Rendered inside the settings popout as a
|
|
52
|
+
* final section, under the four canonical rows. Put your app's own
|
|
53
|
+
* preferences here — checkboxes, selects, whatever it needs; this slot is
|
|
54
|
+
* deliberately unconstrained. What it cannot do is alter the rows above it. */
|
|
55
|
+
children?: React.ReactNode;
|
|
56
|
+
/** Extra class on the <header> root. Layout only — it cannot reach the
|
|
57
|
+
* controls, which own their own markup. */
|
|
58
|
+
className?: string;
|
|
59
|
+
}
|
|
60
|
+
export declare function AppHeader({ title, status, settingsProps, children, className }: AppHeaderProps): React.JSX.Element;
|
|
61
|
+
//# sourceMappingURL=AppHeader.d.ts.map
|