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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +917 -2
  3. package/dist/app-header.css +288 -0
  4. package/dist/bento.css +143 -0
  5. package/dist/button.css +227 -0
  6. package/dist/components/AppHeader.d.ts +61 -0
  7. package/dist/components/AppHeader.d.ts.map +1 -0
  8. package/dist/components/AppHeader.js +23 -0
  9. package/dist/components/AppHeader.js.map +1 -0
  10. package/dist/components/BentoCard.d.ts +23 -0
  11. package/dist/components/BentoCard.d.ts.map +1 -0
  12. package/dist/components/BentoCard.js +49 -0
  13. package/dist/components/BentoCard.js.map +1 -0
  14. package/dist/components/BentoGrid.d.ts +24 -0
  15. package/dist/components/BentoGrid.d.ts.map +1 -0
  16. package/dist/components/BentoGrid.js +22 -0
  17. package/dist/components/BentoGrid.js.map +1 -0
  18. package/dist/components/Button.d.ts +28 -0
  19. package/dist/components/Button.d.ts.map +1 -0
  20. package/dist/components/Button.js +51 -0
  21. package/dist/components/Button.js.map +1 -0
  22. package/dist/components/ConnectedSettings.d.ts +53 -0
  23. package/dist/components/ConnectedSettings.d.ts.map +1 -0
  24. package/dist/components/ConnectedSettings.js +302 -0
  25. package/dist/components/ConnectedSettings.js.map +1 -0
  26. package/dist/components/ConnectedThemePicker.d.ts +52 -0
  27. package/dist/components/ConnectedThemePicker.d.ts.map +1 -0
  28. package/dist/components/ConnectedThemePicker.js +32 -0
  29. package/dist/components/ConnectedThemePicker.js.map +1 -0
  30. package/dist/components/ContextMenu.d.ts +22 -0
  31. package/dist/components/ContextMenu.d.ts.map +1 -0
  32. package/dist/components/ContextMenu.js +23 -0
  33. package/dist/components/ContextMenu.js.map +1 -0
  34. package/dist/components/HadokuThemePicker.d.ts +27 -0
  35. package/dist/components/HadokuThemePicker.d.ts.map +1 -0
  36. package/dist/components/HadokuThemePicker.js +19 -0
  37. package/dist/components/HadokuThemePicker.js.map +1 -0
  38. package/dist/components/LoadingSkeleton.d.ts +14 -0
  39. package/dist/components/LoadingSkeleton.d.ts.map +1 -0
  40. package/dist/components/LoadingSkeleton.js +62 -0
  41. package/dist/components/LoadingSkeleton.js.map +1 -0
  42. package/dist/components/Modal.d.ts +39 -0
  43. package/dist/components/Modal.d.ts.map +1 -0
  44. package/dist/components/Modal.js +106 -0
  45. package/dist/components/Modal.js.map +1 -0
  46. package/dist/components/NumberInput.d.ts +26 -0
  47. package/dist/components/NumberInput.d.ts.map +1 -0
  48. package/dist/components/NumberInput.js +131 -0
  49. package/dist/components/NumberInput.js.map +1 -0
  50. package/dist/components/Select.d.ts +52 -0
  51. package/dist/components/Select.d.ts.map +1 -0
  52. package/dist/components/Select.js +281 -0
  53. package/dist/components/Select.js.map +1 -0
  54. package/dist/components/SettingsModal.d.ts +69 -0
  55. package/dist/components/SettingsModal.d.ts.map +1 -0
  56. package/dist/components/SettingsModal.js +141 -0
  57. package/dist/components/SettingsModal.js.map +1 -0
  58. package/dist/components/ThemeIcons.d.ts +26 -0
  59. package/dist/components/ThemeIcons.d.ts.map +1 -0
  60. package/dist/components/ThemeIcons.js +74 -0
  61. package/dist/components/ThemeIcons.js.map +1 -0
  62. package/dist/components/ThemePicker.d.ts +28 -0
  63. package/dist/components/ThemePicker.d.ts.map +1 -0
  64. package/dist/components/ThemePicker.js +101 -0
  65. package/dist/components/ThemePicker.js.map +1 -0
  66. package/dist/components/Toast.d.ts +13 -0
  67. package/dist/components/Toast.d.ts.map +1 -0
  68. package/dist/components/Toast.js +70 -0
  69. package/dist/components/Toast.js.map +1 -0
  70. package/dist/components/Toaster.d.ts +19 -0
  71. package/dist/components/Toaster.d.ts.map +1 -0
  72. package/dist/components/Toaster.js +19 -0
  73. package/dist/components/Toaster.js.map +1 -0
  74. package/dist/components/Tooltip.d.ts +49 -0
  75. package/dist/components/Tooltip.d.ts.map +1 -0
  76. package/dist/components/Tooltip.js +143 -0
  77. package/dist/components/Tooltip.js.map +1 -0
  78. package/dist/context-menu.css +11 -0
  79. package/dist/controls.css +137 -0
  80. package/dist/hooks/useReducedMotion.d.ts +7 -0
  81. package/dist/hooks/useReducedMotion.d.ts.map +1 -0
  82. package/dist/hooks/useReducedMotion.js +25 -0
  83. package/dist/hooks/useReducedMotion.js.map +1 -0
  84. package/dist/hooks/useToast.d.ts +18 -0
  85. package/dist/hooks/useToast.d.ts.map +1 -0
  86. package/dist/hooks/useToast.js +28 -0
  87. package/dist/hooks/useToast.js.map +1 -0
  88. package/dist/icon-btn.css +64 -0
  89. package/dist/index.d.ts +49 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +72 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/lib/menuPlacement.d.ts +16 -0
  94. package/dist/lib/menuPlacement.d.ts.map +1 -0
  95. package/dist/lib/menuPlacement.js +19 -0
  96. package/dist/lib/menuPlacement.js.map +1 -0
  97. package/dist/lib/selectMenus.d.ts +30 -0
  98. package/dist/lib/selectMenus.d.ts.map +1 -0
  99. package/dist/lib/selectMenus.js +236 -0
  100. package/dist/lib/selectMenus.js.map +1 -0
  101. package/dist/lib/settingsClient.d.ts +120 -0
  102. package/dist/lib/settingsClient.d.ts.map +1 -0
  103. package/dist/lib/settingsClient.js +140 -0
  104. package/dist/lib/settingsClient.js.map +1 -0
  105. package/dist/lib/themeScope.d.ts +19 -0
  106. package/dist/lib/themeScope.d.ts.map +1 -0
  107. package/dist/lib/themeScope.js +10 -0
  108. package/dist/lib/themeScope.js.map +1 -0
  109. package/dist/lib/titleTooltips.d.ts +38 -0
  110. package/dist/lib/titleTooltips.d.ts.map +1 -0
  111. package/dist/lib/titleTooltips.js +166 -0
  112. package/dist/lib/titleTooltips.js.map +1 -0
  113. package/dist/lib/tooltipPlacement.d.ts +14 -0
  114. package/dist/lib/tooltipPlacement.d.ts.map +1 -0
  115. package/dist/lib/tooltipPlacement.js +34 -0
  116. package/dist/lib/tooltipPlacement.js.map +1 -0
  117. package/dist/modal.css +129 -0
  118. package/dist/number-input.css +131 -0
  119. package/dist/scrollbar.css +165 -0
  120. package/dist/select.css +251 -0
  121. package/dist/styles.css +1866 -0
  122. package/dist/task-ui-components.css +1866 -0
  123. package/dist/theme-picker.css +219 -0
  124. package/dist/toaster.css +159 -0
  125. package/dist/tooltip.css +103 -0
  126. package/dist/types/index.d.ts +37 -0
  127. package/dist/types/index.d.ts.map +1 -0
  128. package/package.json +60 -4
  129. package/src/app-header.css +288 -0
  130. package/src/bento.css +143 -0
  131. package/src/button.css +227 -0
  132. package/src/components/AppHeader.tsx +87 -0
  133. package/src/components/BentoCard.tsx +72 -0
  134. package/src/components/BentoGrid.tsx +48 -0
  135. package/src/components/Button.tsx +81 -0
  136. package/src/components/ConnectedSettings.tsx +465 -0
  137. package/src/components/ConnectedThemePicker.tsx +79 -0
  138. package/src/components/ContextMenu.tsx +48 -0
  139. package/src/components/HadokuThemePicker.tsx +39 -0
  140. package/src/components/LoadingSkeleton.tsx +84 -0
  141. package/src/components/Modal.tsx +171 -0
  142. package/src/components/NumberInput.tsx +161 -0
  143. package/src/components/Select.tsx +359 -0
  144. package/src/components/SettingsModal.tsx +235 -0
  145. package/src/components/ThemeIcons.tsx +127 -0
  146. package/src/components/ThemePicker.tsx +149 -0
  147. package/src/components/Toast.tsx +92 -0
  148. package/src/components/Toaster.tsx +43 -0
  149. package/src/components/Tooltip.tsx +212 -0
  150. package/src/context-menu.css +11 -0
  151. package/src/controls.css +137 -0
  152. package/src/hooks/useReducedMotion.ts +36 -0
  153. package/src/hooks/useToast.ts +57 -0
  154. package/src/icon-btn.css +64 -0
  155. package/src/index.ts +140 -0
  156. package/src/lib/menuPlacement.ts +31 -0
  157. package/src/lib/selectMenus.ts +303 -0
  158. package/src/lib/settingsClient.ts +377 -0
  159. package/src/lib/themeScope.ts +23 -0
  160. package/src/lib/titleTooltips.ts +236 -0
  161. package/src/lib/tooltipPlacement.ts +59 -0
  162. package/src/modal.css +129 -0
  163. package/src/number-input.css +131 -0
  164. package/src/scrollbar.css +165 -0
  165. package/src/select.css +251 -0
  166. package/src/theme-picker.css +219 -0
  167. package/src/toaster.css +159 -0
  168. package/src/tooltip.css +103 -0
  169. package/src/types/index.ts +43 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Tooltip Component
3
+ * Themed replacement for `title=`. A native title tooltip is OS-drawn, waits
4
+ * about a second, never appears on keyboard focus and cannot be themed.
5
+ *
6
+ * Meets WCAG 1.4.13 (content on hover or focus): it shows on hover AND focus,
7
+ * Escape dismisses it, and the pointer can move onto the bubble without it
8
+ * closing. The child gets `aria-describedby`, so the text is announced —
9
+ * unless it is the child's own `aria-label`, which would read it twice. That
10
+ * is the common case for an icon button: the label names it, and the tooltip
11
+ * shows sighted users the same words.
12
+ *
13
+ * Like <Select>, the bubble is portalled into the anchor's theme scope (see
14
+ * themeScopeOf), so it keeps the theme's tokens and is placed correctly even
15
+ * under a transformed ancestor.
16
+ */
17
+ import React from 'react';
18
+ import '../tooltip.css';
19
+ import { type TooltipPlacement } from '../lib/tooltipPlacement';
20
+ export type { TooltipPlacement };
21
+ export type TooltipVariant = 'inverse' | 'card' | 'primary';
22
+ export interface TooltipProps {
23
+ content: React.ReactNode;
24
+ /** Preferred side; flips to the opposite one when it would leave the viewport. */
25
+ placement?: TooltipPlacement;
26
+ variant?: TooltipVariant;
27
+ /** Hover delay before showing, ms. Focus shows immediately. */
28
+ delay?: number;
29
+ /** Render the child alone, e.g. while a menu it belongs to is open. */
30
+ disabled?: boolean;
31
+ /** A single element. Tooltip attaches its ref and pointer/focus handlers to it
32
+ * directly (composed with the child's own) and adds aria-describedby. */
33
+ children: React.ReactElement<AnchorProps>;
34
+ }
35
+ /** What Tooltip reads from, and adds to, its child. No wrapper element: a
36
+ * wrapper — even `display: contents` — still changes the DOM tree, and with it
37
+ * every `:last-child`, `>` and `+` selector aimed at the child. */
38
+ interface AnchorProps {
39
+ 'aria-describedby'?: string;
40
+ 'aria-label'?: string;
41
+ ref?: React.Ref<HTMLElement>;
42
+ onPointerEnter?: React.PointerEventHandler<HTMLElement>;
43
+ onPointerLeave?: React.PointerEventHandler<HTMLElement>;
44
+ onPointerDown?: React.PointerEventHandler<HTMLElement>;
45
+ onFocus?: React.FocusEventHandler<HTMLElement>;
46
+ onBlur?: React.FocusEventHandler<HTMLElement>;
47
+ }
48
+ export declare function Tooltip({ content, placement, variant, delay, disabled, children }: TooltipProps): React.JSX.Element;
49
+ //# sourceMappingURL=Tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../src/components/Tooltip.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAA2E,MAAM,OAAO,CAAA;AAE/F,OAAO,gBAAgB,CAAA;AAEvB,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1F,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAChC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAA;AAE3D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,kFAAkF;IAClF,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;8EAC0E;IAC1E,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;CAC1C;AAED;;oEAEoE;AACpE,UAAU,WAAW;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAC5B,cAAc,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAA;IACvD,cAAc,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAA;IACvD,aAAa,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAA;IACtD,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;CAC9C;AAWD,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,SAAiB,EACjB,OAAgB,EAChB,KAAW,EACX,QAAgB,EAChB,QAAQ,EACT,EAAE,YAAY,qBA6Id"}
@@ -0,0 +1,143 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import React, { useId, useState, useRef, useCallback, useEffect, useLayoutEffect } from "react";
3
+ import { createPortal } from "react-dom";
4
+ /* empty css */
5
+ import { themeScopeOf } from "../lib/themeScope.js";
6
+ import { placeTooltip } from "../lib/tooltipPlacement.js";
7
+ function childRefOf(child) {
8
+ if (!React.version.startsWith("18")) return child.props.ref;
9
+ return child.ref ?? void 0;
10
+ }
11
+ const HIDE_GRACE_MS = 120;
12
+ function Tooltip({
13
+ content,
14
+ placement = "top",
15
+ variant = "card",
16
+ delay = 300,
17
+ disabled = false,
18
+ children
19
+ }) {
20
+ const tipId = useId();
21
+ const [open, setOpen] = useState(false);
22
+ const [placed, setPlaced] = useState(null);
23
+ const [scope, setScope] = useState(null);
24
+ const anchorRef = useRef(null);
25
+ const bubbleRef = useRef(null);
26
+ const timer = useRef(void 0);
27
+ const show = useCallback((after) => {
28
+ clearTimeout(timer.current);
29
+ timer.current = setTimeout(() => {
30
+ setScope(themeScopeOf(anchorRef.current));
31
+ setOpen(true);
32
+ }, after);
33
+ }, []);
34
+ const hide = useCallback((after) => {
35
+ clearTimeout(timer.current);
36
+ timer.current = setTimeout(() => {
37
+ setOpen(false);
38
+ setPlaced(null);
39
+ }, after);
40
+ }, []);
41
+ useEffect(() => () => clearTimeout(timer.current), []);
42
+ const childRef = childRefOf(children);
43
+ const setAnchor = useCallback(
44
+ (node) => {
45
+ anchorRef.current = node;
46
+ if (typeof childRef === "function") childRef(node);
47
+ else if (childRef) childRef.current = node;
48
+ },
49
+ [childRef]
50
+ );
51
+ const place = useCallback(() => {
52
+ const target = anchorRef.current;
53
+ const bubble = bubbleRef.current;
54
+ if (!target || !bubble) return;
55
+ setPlaced(
56
+ placeTooltip(
57
+ placement,
58
+ target.getBoundingClientRect(),
59
+ bubble.offsetWidth,
60
+ bubble.offsetHeight
61
+ )
62
+ );
63
+ }, [placement]);
64
+ useLayoutEffect(() => {
65
+ if (open && scope) place();
66
+ }, [open, scope, place, content]);
67
+ useEffect(() => {
68
+ if (!open) return;
69
+ const onKey = (e) => {
70
+ if (e.key === "Escape") hide(0);
71
+ };
72
+ window.addEventListener("scroll", place, true);
73
+ window.addEventListener("resize", place);
74
+ document.addEventListener("keydown", onKey);
75
+ return () => {
76
+ window.removeEventListener("scroll", place, true);
77
+ window.removeEventListener("resize", place);
78
+ document.removeEventListener("keydown", onKey);
79
+ };
80
+ }, [open, place, hide]);
81
+ if (disabled || content == null || content === "") return children;
82
+ const visible = open && placed !== null;
83
+ const restatesLabel = typeof content === "string" && content === children.props["aria-label"];
84
+ const style = placed ? {
85
+ left: placed.left,
86
+ top: placed.top,
87
+ "--hdk-tooltip-arrow": `${placed.arrow}px`
88
+ } : { left: 0, top: 0 };
89
+ const own = children.props;
90
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
91
+ React.cloneElement(children, {
92
+ ref: setAnchor,
93
+ onPointerEnter: (e) => {
94
+ own.onPointerEnter?.(e);
95
+ if (e.pointerType === "mouse") show(delay);
96
+ },
97
+ onPointerLeave: (e) => {
98
+ own.onPointerLeave?.(e);
99
+ if (e.pointerType === "mouse") hide(HIDE_GRACE_MS);
100
+ },
101
+ // A press dismisses it, as it does a native title: the click usually
102
+ // opens something (a menu, a dialog) the bubble would sit on top of.
103
+ onPointerDown: (e) => {
104
+ own.onPointerDown?.(e);
105
+ hide(0);
106
+ },
107
+ onFocus: (e) => {
108
+ own.onFocus?.(e);
109
+ if (e.currentTarget.matches(":focus-visible")) show(0);
110
+ },
111
+ onBlur: (e) => {
112
+ own.onBlur?.(e);
113
+ hide(0);
114
+ },
115
+ "aria-describedby": [own["aria-describedby"], open && !restatesLabel ? tipId : null].filter(Boolean).join(" ") || void 0
116
+ }),
117
+ open && scope && createPortal(
118
+ /* @__PURE__ */ jsx(
119
+ "div",
120
+ {
121
+ ref: bubbleRef,
122
+ id: tipId,
123
+ role: "tooltip",
124
+ className: [
125
+ "hdk-tooltip",
126
+ `hdk-tooltip--${variant}`,
127
+ `hdk-tooltip--${placed?.side ?? placement}`,
128
+ visible && "hdk-tooltip--visible"
129
+ ].filter(Boolean).join(" "),
130
+ style,
131
+ onPointerEnter: () => clearTimeout(timer.current),
132
+ onPointerLeave: () => hide(HIDE_GRACE_MS),
133
+ children: content
134
+ }
135
+ ),
136
+ scope
137
+ )
138
+ ] });
139
+ }
140
+ export {
141
+ Tooltip
142
+ };
143
+ //# sourceMappingURL=Tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.js","sources":["../../src/components/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip Component\n * Themed replacement for `title=`. A native title tooltip is OS-drawn, waits\n * about a second, never appears on keyboard focus and cannot be themed.\n *\n * Meets WCAG 1.4.13 (content on hover or focus): it shows on hover AND focus,\n * Escape dismisses it, and the pointer can move onto the bubble without it\n * closing. The child gets `aria-describedby`, so the text is announced —\n * unless it is the child's own `aria-label`, which would read it twice. That\n * is the common case for an icon button: the label names it, and the tooltip\n * shows sighted users the same words.\n *\n * Like <Select>, the bubble is portalled into the anchor's theme scope (see\n * themeScopeOf), so it keeps the theme's tokens and is placed correctly even\n * under a transformed ancestor.\n */\n\nimport React, { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport '../tooltip.css'\nimport { themeScopeOf } from '../lib/themeScope'\nimport { placeTooltip, type Placed, type TooltipPlacement } from '../lib/tooltipPlacement'\n\nexport type { TooltipPlacement }\nexport type TooltipVariant = 'inverse' | 'card' | 'primary'\n\nexport interface TooltipProps {\n content: React.ReactNode\n /** Preferred side; flips to the opposite one when it would leave the viewport. */\n placement?: TooltipPlacement\n variant?: TooltipVariant\n /** Hover delay before showing, ms. Focus shows immediately. */\n delay?: number\n /** Render the child alone, e.g. while a menu it belongs to is open. */\n disabled?: boolean\n /** A single element. Tooltip attaches its ref and pointer/focus handlers to it\n * directly (composed with the child's own) and adds aria-describedby. */\n children: React.ReactElement<AnchorProps>\n}\n\n/** What Tooltip reads from, and adds to, its child. No wrapper element: a\n * wrapper — even `display: contents` — still changes the DOM tree, and with it\n * every `:last-child`, `>` and `+` selector aimed at the child. */\ninterface AnchorProps {\n 'aria-describedby'?: string\n 'aria-label'?: string\n ref?: React.Ref<HTMLElement>\n onPointerEnter?: React.PointerEventHandler<HTMLElement>\n onPointerLeave?: React.PointerEventHandler<HTMLElement>\n onPointerDown?: React.PointerEventHandler<HTMLElement>\n onFocus?: React.FocusEventHandler<HTMLElement>\n onBlur?: React.FocusEventHandler<HTMLElement>\n}\n\n/** The child's own ref: a prop in React 19, on the element in React 18 (where\n * reading it from props is always undefined, and in 19 `element.ref` warns). */\nfunction childRefOf(child: React.ReactElement<AnchorProps>): React.Ref<HTMLElement> | undefined {\n if (!React.version.startsWith('18')) return child.props.ref\n return (child as unknown as { ref?: React.Ref<HTMLElement> }).ref ?? undefined\n}\n\nconst HIDE_GRACE_MS = 120\n\nexport function Tooltip({\n content,\n placement = 'top',\n variant = 'card',\n delay = 300,\n disabled = false,\n children\n}: TooltipProps) {\n const tipId = useId()\n const [open, setOpen] = useState(false)\n const [placed, setPlaced] = useState<Placed | null>(null)\n const [scope, setScope] = useState<HTMLElement | null>(null)\n const anchorRef = useRef<HTMLElement | null>(null)\n const bubbleRef = useRef<HTMLDivElement>(null)\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n const show = useCallback((after: number) => {\n clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n setScope(themeScopeOf(anchorRef.current))\n setOpen(true)\n }, after)\n }, [])\n\n const hide = useCallback((after: number) => {\n clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n setOpen(false)\n setPlaced(null)\n }, after)\n }, [])\n\n useEffect(() => () => clearTimeout(timer.current), [])\n\n const childRef = childRefOf(children)\n const setAnchor = useCallback(\n (node: HTMLElement | null) => {\n anchorRef.current = node\n if (typeof childRef === 'function') childRef(node)\n else if (childRef) (childRef as React.MutableRefObject<HTMLElement | null>).current = node\n },\n [childRef]\n )\n\n const place = useCallback(() => {\n const target = anchorRef.current\n const bubble = bubbleRef.current\n if (!target || !bubble) return\n setPlaced(\n placeTooltip(\n placement,\n target.getBoundingClientRect(),\n bubble.offsetWidth,\n bubble.offsetHeight\n )\n )\n }, [placement])\n\n useLayoutEffect(() => {\n if (open && scope) place()\n }, [open, scope, place, content])\n\n useEffect(() => {\n if (!open) return\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') hide(0)\n }\n window.addEventListener('scroll', place, true)\n window.addEventListener('resize', place)\n document.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('scroll', place, true)\n window.removeEventListener('resize', place)\n document.removeEventListener('keydown', onKey)\n }\n }, [open, place, hide])\n\n if (disabled || content == null || content === '') return children\n\n const visible = open && placed !== null\n const restatesLabel = typeof content === 'string' && content === children.props['aria-label']\n const style: React.CSSProperties = placed\n ? ({\n left: placed.left,\n top: placed.top,\n '--hdk-tooltip-arrow': `${placed.arrow}px`\n } as React.CSSProperties)\n : { left: 0, top: 0 }\n\n const own = children.props\n return (\n <>\n {React.cloneElement(children, {\n ref: setAnchor,\n onPointerEnter: e => {\n own.onPointerEnter?.(e)\n if (e.pointerType === 'mouse') show(delay)\n },\n onPointerLeave: e => {\n own.onPointerLeave?.(e)\n if (e.pointerType === 'mouse') hide(HIDE_GRACE_MS)\n },\n // A press dismisses it, as it does a native title: the click usually\n // opens something (a menu, a dialog) the bubble would sit on top of.\n onPointerDown: e => {\n own.onPointerDown?.(e)\n hide(0)\n },\n onFocus: e => {\n own.onFocus?.(e)\n // Keyboard focus only — a mouse click focuses too, and the hover\n // path already handles that pointer.\n if (e.currentTarget.matches(':focus-visible')) show(0)\n },\n onBlur: e => {\n own.onBlur?.(e)\n hide(0)\n },\n 'aria-describedby':\n [own['aria-describedby'], open && !restatesLabel ? tipId : null]\n .filter(Boolean)\n .join(' ') || undefined\n })}\n {open &&\n scope &&\n createPortal(\n <div\n ref={bubbleRef}\n id={tipId}\n role=\"tooltip\"\n className={[\n 'hdk-tooltip',\n `hdk-tooltip--${variant}`,\n `hdk-tooltip--${placed?.side ?? placement}`,\n visible && 'hdk-tooltip--visible'\n ]\n .filter(Boolean)\n .join(' ')}\n style={style}\n onPointerEnter={() => clearTimeout(timer.current)}\n onPointerLeave={() => hide(HIDE_GRACE_MS)}\n >\n {content}\n </div>,\n scope\n )}\n </>\n )\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ContextMenu Component Styles
3
+ * Generic context menu with dynamic positioning
4
+ */
5
+
6
+ .context-menu {
7
+ position: fixed;
8
+ left: var(--context-menu-x, 0);
9
+ top: var(--context-menu-y, 0);
10
+ z-index: var(--context-menu-z-index, 1000);
11
+ }
@@ -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,7 @@
1
+ /**
2
+ * useReducedMotion Hook
3
+ * Detects if the user prefers reduced motion
4
+ * Returns true if animations should be disabled or simplified
5
+ */
6
+ export declare function useReducedMotion(): boolean;
7
+ //# sourceMappingURL=useReducedMotion.d.ts.map
@@ -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
+ }
@@ -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, SignInResult, Tier } from './lib/settingsClient';
36
+ export { loadSignIn, 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":"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"}