@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,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 @@
|
|
|
1
|
+
{"version":3,"file":"useReducedMotion.d.ts","sourceRoot":"","sources":["../../src/hooks/useReducedMotion.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,wBAAgB,gBAAgB,IAAI,OAAO,CA2B1C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
function useReducedMotion() {
|
|
3
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => {
|
|
4
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
5
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6
|
+
});
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
9
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
10
|
+
const handleChange = (event) => {
|
|
11
|
+
setPrefersReducedMotion(event.matches);
|
|
12
|
+
};
|
|
13
|
+
if (mediaQuery.addEventListener) {
|
|
14
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
15
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
16
|
+
}
|
|
17
|
+
mediaQuery.addListener(handleChange);
|
|
18
|
+
return () => mediaQuery.removeListener(handleChange);
|
|
19
|
+
}, []);
|
|
20
|
+
return prefersReducedMotion;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
useReducedMotion
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=useReducedMotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useReducedMotion.js","sources":["../../src/hooks/useReducedMotion.ts"],"sourcesContent":["/**\r\n * useReducedMotion Hook\r\n * Detects if the user prefers reduced motion\r\n * Returns true if animations should be disabled or simplified\r\n */\r\n\r\nimport { useState, useEffect } from 'react'\r\n\r\nexport function useReducedMotion(): boolean {\r\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => {\r\n // SSR and jsdom have no matchMedia: default to false.\r\n if (typeof window === 'undefined' || !window.matchMedia) return false\r\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\r\n })\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined' || !window.matchMedia) return\r\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)')\r\n\r\n const handleChange = (event: MediaQueryListEvent) => {\r\n setPrefersReducedMotion(event.matches)\r\n }\r\n\r\n // Modern browsers\r\n if (mediaQuery.addEventListener) {\r\n mediaQuery.addEventListener('change', handleChange)\r\n return () => mediaQuery.removeEventListener('change', handleChange)\r\n }\r\n\r\n // Fallback for older browsers\r\n mediaQuery.addListener(handleChange)\r\n return () => mediaQuery.removeListener(handleChange)\r\n }, [])\r\n\r\n return prefersReducedMotion\r\n}\r\n"],"names":[],"mappings":";AAQO,SAAS,mBAA4B;AAC1C,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,SAAS,MAAM;AAErE,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,WAAY,QAAO;AAChE,WAAO,OAAO,WAAW,kCAAkC,EAAE;AAAA,EAC/D,CAAC;AAED,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,WAAY;AACzD,UAAM,aAAa,OAAO,WAAW,kCAAkC;AAEvE,UAAM,eAAe,CAAC,UAA+B;AACnD,8BAAwB,MAAM,OAAO;AAAA,IACvC;AAGA,QAAI,WAAW,kBAAkB;AAC/B,iBAAW,iBAAiB,UAAU,YAAY;AAClD,aAAO,MAAM,WAAW,oBAAoB,UAAU,YAAY;AAAA,IACpE;AAGA,eAAW,YAAY,YAAY;AACnC,WAAO,MAAM,WAAW,eAAe,YAAY;AAAA,EACrD,GAAG,CAAA,CAAE;AAEL,SAAO;AACT;"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useToast hook
|
|
3
|
+
* Manages toast notification state and display
|
|
4
|
+
*/
|
|
5
|
+
export interface ToastState {
|
|
6
|
+
id: number;
|
|
7
|
+
message: string;
|
|
8
|
+
type: 'success' | 'error' | 'info' | 'warning';
|
|
9
|
+
duration?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface UseToastReturn {
|
|
12
|
+
toasts: ToastState[];
|
|
13
|
+
showToast: (message: string, type?: 'success' | 'error' | 'info' | 'warning', duration?: number) => void;
|
|
14
|
+
dismissToast: (id: number) => void;
|
|
15
|
+
clearAll: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function useToast(): UseToastReturn;
|
|
18
|
+
//# sourceMappingURL=useToast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToast.d.ts","sourceRoot":"","sources":["../../src/hooks/useToast.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,SAAS,EAAE,CACT,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,EAC/C,QAAQ,CAAC,EAAE,MAAM,KACd,IAAI,CAAA;IACT,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB;AAID,wBAAgB,QAAQ,IAAI,cAAc,CA6BzC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
let toastIdCounter = 0;
|
|
3
|
+
function useToast() {
|
|
4
|
+
const [toasts, setToasts] = useState([]);
|
|
5
|
+
const showToast = useCallback(
|
|
6
|
+
(message, type = "info", duration = 3e3) => {
|
|
7
|
+
const id = ++toastIdCounter;
|
|
8
|
+
setToasts((prev) => [...prev, { id, message, type, duration }]);
|
|
9
|
+
},
|
|
10
|
+
[]
|
|
11
|
+
);
|
|
12
|
+
const dismissToast = useCallback((id) => {
|
|
13
|
+
setToasts((prev) => prev.filter((toast) => toast.id !== id));
|
|
14
|
+
}, []);
|
|
15
|
+
const clearAll = useCallback(() => {
|
|
16
|
+
setToasts([]);
|
|
17
|
+
}, []);
|
|
18
|
+
return {
|
|
19
|
+
toasts,
|
|
20
|
+
showToast,
|
|
21
|
+
dismissToast,
|
|
22
|
+
clearAll
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
useToast
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=useToast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToast.js","sources":["../../src/hooks/useToast.ts"],"sourcesContent":["/**\r\n * useToast hook\r\n * Manages toast notification state and display\r\n */\r\n\r\nimport { useState, useCallback } from 'react'\r\n\r\nexport interface ToastState {\r\n id: number\r\n message: string\r\n type: 'success' | 'error' | 'info' | 'warning'\r\n duration?: number\r\n}\r\n\r\nexport interface UseToastReturn {\r\n toasts: ToastState[]\r\n showToast: (\r\n message: string,\r\n type?: 'success' | 'error' | 'info' | 'warning',\r\n duration?: number\r\n ) => void\r\n dismissToast: (id: number) => void\r\n clearAll: () => void\r\n}\r\n\r\nlet toastIdCounter = 0\r\n\r\nexport function useToast(): UseToastReturn {\r\n const [toasts, setToasts] = useState<ToastState[]>([])\r\n\r\n const showToast = useCallback(\r\n (\r\n message: string,\r\n type: 'success' | 'error' | 'info' | 'warning' = 'info',\r\n duration: number = 3000\r\n ) => {\r\n const id = ++toastIdCounter\r\n setToasts(prev => [...prev, { id, message, type, duration }])\r\n },\r\n []\r\n )\r\n\r\n const dismissToast = useCallback((id: number) => {\r\n setToasts(prev => prev.filter(toast => toast.id !== id))\r\n }, [])\r\n\r\n const clearAll = useCallback(() => {\r\n setToasts([])\r\n }, [])\r\n\r\n return {\r\n toasts,\r\n showToast,\r\n dismissToast,\r\n clearAll\r\n }\r\n}\r\n"],"names":[],"mappings":";AAyBA,IAAI,iBAAiB;AAEd,SAAS,WAA2B;AACzC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAuB,CAAA,CAAE;AAErD,QAAM,YAAY;AAAA,IAChB,CACE,SACA,OAAiD,QACjD,WAAmB,QAChB;AACH,YAAM,KAAK,EAAE;AACb,gBAAU,CAAA,SAAQ,CAAC,GAAG,MAAM,EAAE,IAAI,SAAS,MAAM,SAAA,CAAU,CAAC;AAAA,IAC9D;AAAA,IACA,CAAA;AAAA,EAAC;AAGH,QAAM,eAAe,YAAY,CAAC,OAAe;AAC/C,cAAU,UAAQ,KAAK,OAAO,WAAS,MAAM,OAAO,EAAE,CAAC;AAAA,EACzD,GAAG,CAAA,CAAE;AAEL,QAAM,WAAW,YAAY,MAAM;AACjC,cAAU,CAAA,CAAE;AAAA,EACd,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
export { HadokuThemePicker } from './components/HadokuThemePicker';
|
|
9
|
+
export { AppHeader } from './components/AppHeader';
|
|
10
|
+
export { ConnectedSettings } from './components/ConnectedSettings';
|
|
11
|
+
export { HadokuThemeProvider, useHadokuTheme, useHadokuThemeOptional } from '@wolffm/themes';
|
|
12
|
+
export type { HadokuThemeValue, HadokuThemeProviderProps } from '@wolffm/themes';
|
|
13
|
+
export { Toast } from './components/Toast';
|
|
14
|
+
export { Toaster } from './components/Toaster';
|
|
15
|
+
export { Modal } from './components/Modal';
|
|
16
|
+
export { ContextMenu } from './components/ContextMenu';
|
|
17
|
+
export { LoadingSkeleton } from './components/LoadingSkeleton';
|
|
18
|
+
export { SettingsModal } from './components/SettingsModal';
|
|
19
|
+
export { Button } from './components/Button';
|
|
20
|
+
export { Select, NativeSelect } from './components/Select';
|
|
21
|
+
export { NumberInput } from './components/NumberInput';
|
|
22
|
+
export { Tooltip } from './components/Tooltip';
|
|
23
|
+
export { installTitleTooltips } from './lib/titleTooltips';
|
|
24
|
+
export type { TitleTooltipOptions } from './lib/titleTooltips';
|
|
25
|
+
export { installSelectMenus } from './lib/selectMenus';
|
|
26
|
+
export { BentoGrid } from './components/BentoGrid';
|
|
27
|
+
export { BentoCard } from './components/BentoCard';
|
|
28
|
+
export { useToast } from './hooks/useToast';
|
|
29
|
+
export { useReducedMotion } from './hooks/useReducedMotion';
|
|
30
|
+
export { SettingsIcon, TagIcon, CircleIcon, SquareIcon, TriangleIcon, DiamondIcon, StarIcon, HexagonIcon, PentagonIcon, OctagonIcon, FALLBACK_ICONS, getFallbackIcon } from './components/ThemeIcons';
|
|
31
|
+
export { SunIcon, MoonIcon, StrawberryIcon, WaveIcon, ZapIcon, CoffeeIcon, FlowerIcon, HeartIcon, LeafIcon, LanternIcon } from '@wolffm/themes';
|
|
32
|
+
export type { ThemeName, ThemeFamily, DropdownPlacement, ThemeMode } from './types';
|
|
33
|
+
export type { AppHeaderProps } from './components/AppHeader';
|
|
34
|
+
export type { ConnectedSettingsProps } from './components/ConnectedSettings';
|
|
35
|
+
export type { ContentLevelState, Identity, SignIn, SignInClient, SignInResult, Tier } from './lib/settingsClient';
|
|
36
|
+
export { loadSignIn, loadSignInClient, passkeyHref, passkeysSupported, switchKeyHref } from './lib/settingsClient';
|
|
37
|
+
export type { ToastProps } from './components/Toast';
|
|
38
|
+
export type { ToasterProps, ToastState } from './components/Toaster';
|
|
39
|
+
export type { UseToastReturn } from './hooks/useToast';
|
|
40
|
+
export type { ModalProps } from './components/Modal';
|
|
41
|
+
export type { ContextMenuProps, ContextMenuItem } from './components/ContextMenu';
|
|
42
|
+
export type { LoadingSkeletonProps } from './components/LoadingSkeleton';
|
|
43
|
+
export type { ButtonProps, ButtonVariant, ButtonFamily, ButtonSize, ButtonShape } from './components/Button';
|
|
44
|
+
export type { SelectProps, SelectOption, SelectVariant, SelectSize, NativeSelectProps } from './components/Select';
|
|
45
|
+
export type { TooltipProps, TooltipPlacement, TooltipVariant } from './components/Tooltip';
|
|
46
|
+
export type { SettingsModalProps, SettingsSection, SettingsField, SettingsToggleField, SettingsTextInputField, SettingsPasswordField, SettingsSelectField, SettingsButtonField, SettingsCustomField } from './components/SettingsModal';
|
|
47
|
+
export type { BentoGridProps, BentoColumns } from './components/BentoGrid';
|
|
48
|
+
export type { BentoCardProps, CardSize } from './components/BentoCard';
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAOlE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAC5F,YAAY,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAChF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAI1D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAG9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAG9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAGtD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAGlD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAG3D,OAAO,EACL,YAAY,EACZ,OAAO,EAEP,UAAU,EACV,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,EAChB,MAAM,yBAAyB,CAAA;AAGhC,OAAO,EACL,OAAO,EACP,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,OAAO,EACP,UAAU,EACV,UAAU,EACV,SAAS,EACT,QAAQ,EACR,WAAW,EACZ,MAAM,gBAAgB,CAAA;AAMvB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnF,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,YAAY,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAC5E,YAAY,EACV,iBAAiB,EACjB,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,IAAI,EACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,aAAa,EACd,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACpE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACtD,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACjF,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AACxE,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,UAAU,EACV,WAAW,EACZ,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,UAAU,EACV,iBAAiB,EAClB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1F,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAC1E,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { HadokuThemePicker } from "./components/HadokuThemePicker.js";
|
|
2
|
+
import { AppHeader } from "./components/AppHeader.js";
|
|
3
|
+
import { ConnectedSettings } from "./components/ConnectedSettings.js";
|
|
4
|
+
import { CoffeeIcon, FlowerIcon, HadokuThemeProvider, HeartIcon, LanternIcon, LeafIcon, MoonIcon, StrawberryIcon, SunIcon, WaveIcon, ZapIcon, useHadokuTheme, useHadokuThemeOptional } from "@wolffm/themes";
|
|
5
|
+
import { Toast } from "./components/Toast.js";
|
|
6
|
+
import { Toaster } from "./components/Toaster.js";
|
|
7
|
+
import { Modal } from "./components/Modal.js";
|
|
8
|
+
import { ContextMenu } from "./components/ContextMenu.js";
|
|
9
|
+
import { LoadingSkeleton } from "./components/LoadingSkeleton.js";
|
|
10
|
+
import { SettingsModal } from "./components/SettingsModal.js";
|
|
11
|
+
import { Button } from "./components/Button.js";
|
|
12
|
+
import { NativeSelect, Select } from "./components/Select.js";
|
|
13
|
+
import { NumberInput } from "./components/NumberInput.js";
|
|
14
|
+
import { Tooltip } from "./components/Tooltip.js";
|
|
15
|
+
import { installTitleTooltips } from "./lib/titleTooltips.js";
|
|
16
|
+
import { installSelectMenus } from "./lib/selectMenus.js";
|
|
17
|
+
import { BentoGrid } from "./components/BentoGrid.js";
|
|
18
|
+
import { BentoCard } from "./components/BentoCard.js";
|
|
19
|
+
import { useToast } from "./hooks/useToast.js";
|
|
20
|
+
import { useReducedMotion } from "./hooks/useReducedMotion.js";
|
|
21
|
+
import { CircleIcon, DiamondIcon, FALLBACK_ICONS, HexagonIcon, OctagonIcon, PentagonIcon, SettingsIcon, SquareIcon, StarIcon, TagIcon, TriangleIcon, getFallbackIcon } from "./components/ThemeIcons.js";
|
|
22
|
+
import { loadSignIn, loadSignInClient, passkeyHref, passkeysSupported, switchKeyHref } from "./lib/settingsClient.js";
|
|
23
|
+
export {
|
|
24
|
+
AppHeader,
|
|
25
|
+
BentoCard,
|
|
26
|
+
BentoGrid,
|
|
27
|
+
Button,
|
|
28
|
+
CircleIcon,
|
|
29
|
+
CoffeeIcon,
|
|
30
|
+
ConnectedSettings,
|
|
31
|
+
ContextMenu,
|
|
32
|
+
DiamondIcon,
|
|
33
|
+
FALLBACK_ICONS,
|
|
34
|
+
FlowerIcon,
|
|
35
|
+
HadokuThemePicker,
|
|
36
|
+
HadokuThemeProvider,
|
|
37
|
+
HeartIcon,
|
|
38
|
+
HexagonIcon,
|
|
39
|
+
LanternIcon,
|
|
40
|
+
LeafIcon,
|
|
41
|
+
LoadingSkeleton,
|
|
42
|
+
Modal,
|
|
43
|
+
MoonIcon,
|
|
44
|
+
NativeSelect,
|
|
45
|
+
NumberInput,
|
|
46
|
+
OctagonIcon,
|
|
47
|
+
PentagonIcon,
|
|
48
|
+
Select,
|
|
49
|
+
SettingsIcon,
|
|
50
|
+
SettingsModal,
|
|
51
|
+
SquareIcon,
|
|
52
|
+
StarIcon,
|
|
53
|
+
StrawberryIcon,
|
|
54
|
+
SunIcon,
|
|
55
|
+
TagIcon,
|
|
56
|
+
Toast,
|
|
57
|
+
Toaster,
|
|
58
|
+
Tooltip,
|
|
59
|
+
TriangleIcon,
|
|
60
|
+
WaveIcon,
|
|
61
|
+
ZapIcon,
|
|
62
|
+
getFallbackIcon,
|
|
63
|
+
installSelectMenus,
|
|
64
|
+
installTitleTooltips,
|
|
65
|
+
loadSignIn,
|
|
66
|
+
loadSignInClient,
|
|
67
|
+
passkeyHref,
|
|
68
|
+
passkeysSupported,
|
|
69
|
+
switchKeyHref,
|
|
70
|
+
useHadokuTheme,
|
|
71
|
+
useHadokuThemeOptional,
|
|
72
|
+
useReducedMotion,
|
|
73
|
+
useToast
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
export declare const MENU_GAP = 4;
|
|
6
|
+
export declare const MENU_MAX_HEIGHT = 280;
|
|
7
|
+
export interface MenuPosition {
|
|
8
|
+
left: number;
|
|
9
|
+
width: number;
|
|
10
|
+
top?: number;
|
|
11
|
+
bottom?: number;
|
|
12
|
+
maxHeight: number;
|
|
13
|
+
}
|
|
14
|
+
/** `listHeight` is the list's full scrollHeight, so a short list never flips. */
|
|
15
|
+
export declare function placeMenu(rect: DOMRect, listHeight: number): MenuPosition;
|
|
16
|
+
//# sourceMappingURL=menuPlacement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menuPlacement.d.ts","sourceRoot":"","sources":["../../src/lib/menuPlacement.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,QAAQ,IAAI,CAAA;AACzB,eAAO,MAAM,eAAe,MAAM,CAAA;AAElC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CAazE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const MENU_GAP = 4;
|
|
2
|
+
const MENU_MAX_HEIGHT = 280;
|
|
3
|
+
function placeMenu(rect, listHeight) {
|
|
4
|
+
const below = window.innerHeight - rect.bottom - MENU_GAP;
|
|
5
|
+
const above = rect.top - MENU_GAP;
|
|
6
|
+
const flip = below < Math.min(MENU_MAX_HEIGHT, listHeight) && above > below;
|
|
7
|
+
return {
|
|
8
|
+
left: rect.left,
|
|
9
|
+
width: rect.width,
|
|
10
|
+
...flip ? { bottom: window.innerHeight - rect.top + MENU_GAP } : { top: rect.bottom + MENU_GAP },
|
|
11
|
+
maxHeight: Math.max(120, Math.min(MENU_MAX_HEIGHT, flip ? above : below) - 8)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
MENU_GAP,
|
|
16
|
+
MENU_MAX_HEIGHT,
|
|
17
|
+
placeMenu
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=menuPlacement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menuPlacement.js","sources":["../../src/lib/menuPlacement.ts"],"sourcesContent":["/**\n * Where a dropdown list goes under (or over) its trigger, shared by <Select>\n * and installSelectMenus so both lists sit and flip identically.\n */\n\nexport const MENU_GAP = 4\nexport const MENU_MAX_HEIGHT = 280\n\nexport interface MenuPosition {\n left: number\n width: number\n top?: number\n bottom?: number\n maxHeight: number\n}\n\n/** `listHeight` is the list's full scrollHeight, so a short list never flips. */\nexport function placeMenu(rect: DOMRect, listHeight: number): MenuPosition {\n const below = window.innerHeight - rect.bottom - MENU_GAP\n const above = rect.top - MENU_GAP\n // Flip up only when the list does not fit below AND there is more room above.\n const flip = below < Math.min(MENU_MAX_HEIGHT, listHeight) && above > below\n return {\n left: rect.left,\n width: rect.width,\n ...(flip\n ? { bottom: window.innerHeight - rect.top + MENU_GAP }\n : { top: rect.bottom + MENU_GAP }),\n maxHeight: Math.max(120, Math.min(MENU_MAX_HEIGHT, flip ? above : below) - 8)\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,WAAW;AACjB,MAAM,kBAAkB;AAWxB,SAAS,UAAU,MAAe,YAAkC;AACzE,QAAM,QAAQ,OAAO,cAAc,KAAK,SAAS;AACjD,QAAM,QAAQ,KAAK,MAAM;AAEzB,QAAM,OAAO,QAAQ,KAAK,IAAI,iBAAiB,UAAU,KAAK,QAAQ;AACtE,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,GAAI,OACA,EAAE,QAAQ,OAAO,cAAc,KAAK,MAAM,SAAA,IAC1C,EAAE,KAAK,KAAK,SAAS,SAAA;AAAA,IACzB,WAAW,KAAK,IAAI,KAAK,KAAK,IAAI,iBAAiB,OAAO,QAAQ,KAAK,IAAI,CAAC;AAAA,EAAA;AAEhF;"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The themed list for every native <select> on a page, with no change to the
|
|
3
|
+
* markup.
|
|
4
|
+
*
|
|
5
|
+
* import { installSelectMenus } from '@wolffm/task-ui-components/select-menus'
|
|
6
|
+
* import '@wolffm/task-ui-components/select.css'
|
|
7
|
+
* installSelectMenus()
|
|
8
|
+
*
|
|
9
|
+
* hadoku_site calls this once from its shell, which covers every
|
|
10
|
+
* micro-frontend; an app does not call it itself. Framework-free and
|
|
11
|
+
* React-free, like installTitleTooltips, for the same reason.
|
|
12
|
+
*
|
|
13
|
+
* A native select's closed box takes CSS, but its open list is drawn by the
|
|
14
|
+
* OS and takes almost none. So a mouse press or an opening key on the select
|
|
15
|
+
* is intercepted (preventDefault on mousedown is what stops the OS list) and
|
|
16
|
+
* the same list <Select> renders opens instead, built from the select's own
|
|
17
|
+
* <option>s and <optgroup>s at that moment.
|
|
18
|
+
*
|
|
19
|
+
* The <select> stays the source of truth. Choosing sets its selectedIndex and
|
|
20
|
+
* fires `input` and `change` exactly as the OS list would, so every framework
|
|
21
|
+
* (React's onChange included) and every plain listener sees an ordinary
|
|
22
|
+
* change, and a form still submits the select's value.
|
|
23
|
+
*
|
|
24
|
+
* Left native on purpose:
|
|
25
|
+
* - touch: a phone's own picker is the better control there
|
|
26
|
+
* - `multiple` or `size > 1`, which render as an inline list, not a popup
|
|
27
|
+
* - anything under `data-native-select`
|
|
28
|
+
*/
|
|
29
|
+
export declare function installSelectMenus(): () => void;
|
|
30
|
+
//# sourceMappingURL=selectMenus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectMenus.d.ts","sourceRoot":"","sources":["../../src/lib/selectMenus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AA0BH,wBAAgB,kBAAkB,IAAI,MAAM,IAAI,CAyP/C"}
|