@expcat/tigercat-react 2.0.19 → 2.1.1

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 (186) hide show
  1. package/README.md +25 -16
  2. package/dist/chunk-246DAUEF.mjs +181 -0
  3. package/dist/{chunk-PU3JY6RE.mjs → chunk-2JMI7TCM.mjs} +1 -1
  4. package/dist/chunk-2YBLMVDT.mjs +428 -0
  5. package/dist/chunk-2YOP55OF.mjs +639 -0
  6. package/dist/{chunk-25D5TXZH.mjs → chunk-32MCQMBJ.mjs} +8 -6
  7. package/dist/chunk-3HHEVMHE.mjs +180 -0
  8. package/dist/{chunk-HSBN5MLS.mjs → chunk-3LACOSJQ.mjs} +1 -1
  9. package/dist/{chunk-F6WV53BS.mjs → chunk-3LDDXHW2.mjs} +28 -11
  10. package/dist/{chunk-IXQAECFP.mjs → chunk-3QNIFKVF.mjs} +12 -14
  11. package/dist/{chunk-UD4DTVJZ.mjs → chunk-4C2X5PZO.mjs} +57 -25
  12. package/dist/chunk-5JETWHLJ.mjs +28 -0
  13. package/dist/{chunk-VA6PZWOH.mjs → chunk-5SEMRHBD.mjs} +14 -6
  14. package/dist/{chunk-EKILTLUO.mjs → chunk-72CJNKDY.mjs} +1 -1
  15. package/dist/{chunk-XE4YFBOZ.mjs → chunk-AUD3VOLO.mjs} +1 -1
  16. package/dist/{chunk-UT5BV2VO.mjs → chunk-AYFKHWJS.mjs} +16 -10
  17. package/dist/{chunk-RPSHILHD.mjs → chunk-BXAH2GH3.mjs} +8 -2
  18. package/dist/{chunk-EW5EO7VE.mjs → chunk-BYD7EAWK.mjs} +1 -1
  19. package/dist/{chunk-BOGOQ44C.mjs → chunk-CQOLQAB5.mjs} +105 -90
  20. package/dist/{chunk-L55UK45A.mjs → chunk-CRIEUKMD.mjs} +5 -2
  21. package/dist/{chunk-DKNVSQSV.mjs → chunk-CUAXRBKO.mjs} +12 -0
  22. package/dist/{chunk-E7OTDBI6.mjs → chunk-DECUASRW.mjs} +17 -19
  23. package/dist/{chunk-XMTEMBQV.mjs → chunk-EAAWB3CK.mjs} +16 -13
  24. package/dist/{chunk-D33BIMGK.mjs → chunk-EG7EMDKK.mjs} +12 -2
  25. package/dist/chunk-EW43YBPV.mjs +457 -0
  26. package/dist/{chunk-HOYBHB7B.mjs → chunk-FNK2VWPP.mjs} +1 -1
  27. package/dist/{chunk-RJPNBYK5.mjs → chunk-FQS24AUH.mjs} +12 -3
  28. package/dist/{chunk-LADKKOJT.mjs → chunk-GBEDWXLL.mjs} +16 -10
  29. package/dist/chunk-GR3Y3XWP.mjs +47 -0
  30. package/dist/{chunk-2ENQZECC.mjs → chunk-HAXRHSCN.mjs} +4 -3
  31. package/dist/{chunk-K4GIKSZE.mjs → chunk-I2BENK5X.mjs} +2 -1
  32. package/dist/chunk-IGBQGR6G.mjs +234 -0
  33. package/dist/chunk-J76R2S62.mjs +161 -0
  34. package/dist/{chunk-DNOPEJ5I.mjs → chunk-JZZPLKTW.mjs} +7 -4
  35. package/dist/{chunk-7KCQMXJA.mjs → chunk-KDURW5AS.mjs} +4 -3
  36. package/dist/{chunk-TFMEHUVZ.mjs → chunk-KKN7KLTB.mjs} +0 -1
  37. package/dist/{chunk-6MGQAJ62.mjs → chunk-KL6YJIZ3.mjs} +24 -7
  38. package/dist/chunk-KRDHM5KB.mjs +259 -0
  39. package/dist/{chunk-HHIW7V7B.mjs → chunk-M6ZPTGBV.mjs} +2 -10
  40. package/dist/chunk-MFPVVGNA.mjs +248 -0
  41. package/dist/{chunk-PXRZ6CKY.mjs → chunk-MJVSZ2UJ.mjs} +46 -16
  42. package/dist/{chunk-LZKBDQE4.mjs → chunk-MYI3FPG4.mjs} +23 -4
  43. package/dist/{chunk-OIGEBCAS.mjs → chunk-NADODEZX.mjs} +69 -1
  44. package/dist/{chunk-KMP5CVNK.mjs → chunk-NJWTZ5MO.mjs} +57 -45
  45. package/dist/chunk-NLGLBTSM.mjs +138 -0
  46. package/dist/chunk-NT7GW2FD.mjs +40 -0
  47. package/dist/{chunk-773LH7BK.mjs → chunk-O6VYD4E4.mjs} +24 -2
  48. package/dist/{chunk-G7D4QV7A.mjs → chunk-O7TV5OKS.mjs} +33 -6
  49. package/dist/{chunk-DPWRXXXW.mjs → chunk-OJ6SHHOZ.mjs} +3 -3
  50. package/dist/chunk-OKYMTHFQ.mjs +279 -0
  51. package/dist/{chunk-UYJJLHVA.mjs → chunk-PMQJ6AYT.mjs} +23 -21
  52. package/dist/chunk-PY2VUNEJ.mjs +494 -0
  53. package/dist/{chunk-BEJZPJIK.mjs → chunk-QOAOOD53.mjs} +10 -4
  54. package/dist/{chunk-2WGEHH5A.mjs → chunk-QU3QXUTC.mjs} +49 -23
  55. package/dist/{chunk-V6P7ZK6O.mjs → chunk-QXSUG7SN.mjs} +3 -3
  56. package/dist/{chunk-FYINJRYN.mjs → chunk-R7YZ6FKP.mjs} +27 -16
  57. package/dist/{chunk-YI6YWH66.mjs → chunk-RE5ZLA5D.mjs} +6 -6
  58. package/dist/{chunk-UCBW5RJN.mjs → chunk-RGVU4EMA.mjs} +11 -6
  59. package/dist/{chunk-IBSOUQGB.mjs → chunk-RRGPDFDI.mjs} +7 -4
  60. package/dist/{chunk-ZH5ZBLAN.mjs → chunk-RUQ3F3MV.mjs} +34 -7
  61. package/dist/chunk-SEOG5AGS.mjs +72 -0
  62. package/dist/{chunk-PGD5RPKF.mjs → chunk-SVIJ2UZA.mjs} +2 -1
  63. package/dist/chunk-SXWEFZOF.mjs +766 -0
  64. package/dist/{chunk-NCHUEGUW.mjs → chunk-TEVEXDY2.mjs} +27 -16
  65. package/dist/{chunk-DJOFM2VO.mjs → chunk-TO6MZFNQ.mjs} +52 -40
  66. package/dist/chunk-TQ6RIFWR.mjs +135 -0
  67. package/dist/chunk-U4M3DC2P.mjs +48 -0
  68. package/dist/{chunk-ZCGIZOMT.mjs → chunk-V7HJ5HLC.mjs} +6 -10
  69. package/dist/{chunk-SIDDYP3O.mjs → chunk-VJWSKJLJ.mjs} +1 -1
  70. package/dist/{chunk-ZG56T75O.mjs → chunk-WTHWLCRL.mjs} +51 -11
  71. package/dist/{chunk-KP6DEYWB.mjs → chunk-X4L3W7RQ.mjs} +1 -1
  72. package/dist/{chunk-ZMRTETN2.mjs → chunk-X6C6MNG5.mjs} +4 -3
  73. package/dist/chunk-XT43B7ZI.mjs +58 -0
  74. package/dist/{chunk-OGG2TQDG.mjs → chunk-XVHTJQ4T.mjs} +12 -1
  75. package/dist/{chunk-QJE3MZBO.mjs → chunk-Y6UH3EKE.mjs} +1 -1
  76. package/dist/chunk-YJ5T4XZN.mjs +81 -0
  77. package/dist/{chunk-VVNFXJMN.mjs → chunk-YNDPQVEK.mjs} +8 -1
  78. package/dist/{chunk-QGZ32NFQ.mjs → chunk-Z23KOLNU.mjs} +4 -3
  79. package/dist/{chunk-5CKMYMTS.mjs → chunk-ZATZRXMW.mjs} +27 -8
  80. package/dist/components/ActivityFeed.mjs +3 -3
  81. package/dist/components/Affix.mjs +1 -1
  82. package/dist/components/Anchor.mjs +1 -1
  83. package/dist/components/AreaChart.mjs +5 -4
  84. package/dist/components/AspectRatio.d.mts +10 -0
  85. package/dist/components/AspectRatio.mjs +8 -0
  86. package/dist/components/AutoComplete.mjs +1 -1
  87. package/dist/components/BarChart.mjs +5 -4
  88. package/dist/components/Calendar.mjs +1 -1
  89. package/dist/components/Cascader.mjs +1 -1
  90. package/dist/components/ChartCanvas.mjs +1 -1
  91. package/dist/components/ChatWindow.mjs +4 -4
  92. package/dist/components/Code.mjs +3 -1
  93. package/dist/components/CollapsePanel.mjs +1 -1
  94. package/dist/components/ColorPicker.mjs +1 -1
  95. package/dist/components/CommentThread.mjs +2 -2
  96. package/dist/components/ContextMenu.d.mts +36 -0
  97. package/dist/components/ContextMenu.mjs +17 -0
  98. package/dist/components/DataExport.mjs +2 -2
  99. package/dist/components/DataTableWithToolbar.mjs +7 -7
  100. package/dist/components/DonutChart.mjs +4 -4
  101. package/dist/components/Dropdown.mjs +1 -1
  102. package/dist/components/FileManager.mjs +2 -1
  103. package/dist/components/FloatButton.mjs +1 -1
  104. package/dist/components/FormItem.mjs +1 -1
  105. package/dist/components/FormWizard.mjs +1 -1
  106. package/dist/components/FunnelChart.mjs +3 -3
  107. package/dist/components/Gantt.d.mts +3 -1
  108. package/dist/components/Gantt.mjs +2 -2
  109. package/dist/components/GaugeChart.mjs +2 -2
  110. package/dist/components/HeatmapChart.mjs +3 -3
  111. package/dist/components/Highlight.d.mts +11 -0
  112. package/dist/components/Highlight.mjs +8 -0
  113. package/dist/components/Image.mjs +4 -4
  114. package/dist/components/ImageCompare.d.mts +15 -0
  115. package/dist/components/ImageCompare.mjs +8 -0
  116. package/dist/components/ImageGroup.mjs +2 -2
  117. package/dist/components/ImagePreview.mjs +1 -1
  118. package/dist/components/Input.mjs +1 -1
  119. package/dist/components/InputNumber.d.mts +1 -1
  120. package/dist/components/InputNumber.mjs +1 -1
  121. package/dist/components/InputOTP.d.mts +33 -0
  122. package/dist/components/InputOTP.mjs +12 -0
  123. package/dist/components/Kanban.mjs +2 -2
  124. package/dist/components/Kbd.d.mts +10 -0
  125. package/dist/components/Kbd.mjs +8 -0
  126. package/dist/components/LineChart.mjs +5 -4
  127. package/dist/components/List.mjs +2 -2
  128. package/dist/components/Loading.mjs +1 -1
  129. package/dist/components/LoadingBar.d.mts +8 -0
  130. package/dist/components/LoadingBar.mjs +138 -0
  131. package/dist/components/LoadingBarContainer.d.mts +8 -0
  132. package/dist/components/LoadingBarContainer.mjs +8 -0
  133. package/dist/components/LoadingBarRoot.d.mts +6 -0
  134. package/dist/components/LoadingBarRoot.mjs +6 -0
  135. package/dist/components/Marquee.d.mts +10 -0
  136. package/dist/components/Marquee.mjs +8 -0
  137. package/dist/components/MaskInput.d.mts +37 -0
  138. package/dist/components/MaskInput.mjs +10 -0
  139. package/dist/components/Masonry.d.mts +10 -0
  140. package/dist/components/Masonry.mjs +8 -0
  141. package/dist/components/NavigationMenu.d.mts +84 -0
  142. package/dist/components/NavigationMenu.mjs +23 -0
  143. package/dist/components/NotificationCenter.mjs +4 -4
  144. package/dist/components/OrgChart.mjs +2 -2
  145. package/dist/components/PageHeader.d.mts +34 -0
  146. package/dist/components/PageHeader.mjs +11 -0
  147. package/dist/components/Pagination.mjs +1 -1
  148. package/dist/components/PieChart.mjs +3 -3
  149. package/dist/components/Popconfirm.mjs +2 -2
  150. package/dist/components/Popover.mjs +2 -2
  151. package/dist/components/QRCode.d.mts +2 -2
  152. package/dist/components/QRCode.mjs +1 -1
  153. package/dist/components/RadarChart.mjs +4 -4
  154. package/dist/components/Rate.mjs +1 -1
  155. package/dist/components/ScatterChart.mjs +5 -4
  156. package/dist/components/ScrollArea.d.mts +10 -0
  157. package/dist/components/ScrollArea.mjs +8 -0
  158. package/dist/components/ScrollSpy.mjs +1 -1
  159. package/dist/components/Signature.mjs +1 -1
  160. package/dist/components/SplitButton.d.mts +38 -0
  161. package/dist/components/SplitButton.mjs +12 -0
  162. package/dist/components/Splitter.mjs +1 -1
  163. package/dist/components/Stepper.d.mts +2 -0
  164. package/dist/components/Stepper.mjs +2 -1
  165. package/dist/components/SunburstChart.mjs +3 -3
  166. package/dist/components/Switch.d.mts +5 -1
  167. package/dist/components/Switch.mjs +2 -1
  168. package/dist/components/Table.mjs +2 -2
  169. package/dist/components/TagsInput.d.mts +41 -0
  170. package/dist/components/TagsInput.mjs +13 -0
  171. package/dist/components/TaskBoard.mjs +1 -1
  172. package/dist/components/Textarea.mjs +1 -1
  173. package/dist/components/Tooltip.mjs +2 -2
  174. package/dist/components/Tour.mjs +1 -1
  175. package/dist/components/TreeMapChart.mjs +3 -3
  176. package/dist/components/TreeSelect.mjs +2 -1
  177. package/dist/components/Upload.mjs +1 -1
  178. package/dist/hooks/useChartInteraction.d.mts +5 -0
  179. package/dist/hooks/useChartInteraction.mjs +1 -1
  180. package/dist/index.d.mts +17 -1
  181. package/dist/index.mjs +406 -0
  182. package/package.json +117 -2
  183. package/dist/chunk-7AFRHC7P.mjs +0 -312
  184. package/dist/chunk-CB4SK5AU.mjs +0 -235
  185. package/dist/chunk-KPACLRQH.mjs +0 -55
  186. package/dist/chunk-YIB6ZXZF.mjs +0 -386
package/README.md CHANGED
@@ -17,7 +17,7 @@ pnpm add @expcat/tigercat-react
17
17
  npm install @expcat/tigercat-react
18
18
  ```
19
19
 
20
- **Requirements:** React 19, Tailwind CSS ≥3.4
20
+ **Requirements:** React / React DOM 19.x, Tailwind CSS 4.x, Node.js 22.13.0
21
21
 
22
22
  ## Quick Start
23
23
 
@@ -35,24 +35,33 @@ function App() {
35
35
 
36
36
  ## Components
37
37
 
38
- | Category | Components |
39
- | -------------- | ------------------------------------------------------------------------------------- |
40
- | **Basic** | Alert, Avatar, Badge, Button, Code, Divider, Icon, Link, Tag, Text |
41
- | **Form** | Input, Select, Checkbox, Radio, Switch, Slider, DatePicker, TimePicker, Upload, Form |
42
- | **Layout** | Card, Container, Grid (Row/Col), Layout, List, Descriptions, Skeleton, Space |
43
- | **Navigation** | Menu, Tabs, Breadcrumb, Pagination, Steps, Dropdown, Tree |
44
- | **Feedback** | Modal, Drawer, Message, Notification, Loading, Popconfirm, Popover, Tooltip, Progress |
45
- | **Data** | Table, Timeline |
46
- | **Charts** | BarChart, LineChart, AreaChart, PieChart, DonutChart, RadarChart, ScatterChart |
38
+ 172 public component entries across 9 categories. The authoritative list with
39
+ import subpaths lives in
40
+ [component-index.md](https://github.com/expcat/Tigercat/blob/main/skills/tigercat/references/component-index.md)
41
+ (generated); the table below is only a sampler.
42
+
43
+ | Category | Count | Examples |
44
+ | -------------- | ----- | ------------------------------------------------------------- |
45
+ | **Basic** | 30 | Button, Alert, Avatar, Badge, Icon, Tag, Text, Result, QRCode |
46
+ | **Form** | 31 | Input, Select, DatePicker, Form, Upload, InputOTP, TagsInput |
47
+ | **Layout** | 19 | Card, Row/Col, Layout, List, Descriptions, Skeleton, Space |
48
+ | **Navigation** | 33 | Menu, Tabs, Breadcrumb, Pagination, Steps, Dropdown, Tree |
49
+ | **Feedback** | 13 | Modal, Drawer, Message, Popover, Tooltip, Progress, Tour |
50
+ | **Data** | 7 | Table, Calendar, Collapse, Timeline, DataExport |
51
+ | **Charts** | 20 | BarChart, LineChart, PieChart, RadarChart, GaugeChart, Gantt |
52
+ | **Advanced** | 12 | VirtualTable, VirtualList, RichTextEditor, CodeEditor |
53
+ | **Composite** | 7 | DataTableWithToolbar, ChatWindow, FormWizard, TaskBoard |
54
+
55
+ `Message` and `notification` are also available as imperative command APIs from
56
+ the package root; `notification` is a command API, not a component.
47
57
 
48
58
  ## Hooks
49
59
 
50
- - `useChartInteraction` - Chart interaction utilities
51
- - `useFormContext` - Form state management
52
- - `useMenuContext` - Menu state access
53
- - `useTabsContext` - Tabs state access
54
- - `useBreadcrumbContext` - Breadcrumb navigation
55
- - `useStepsContext` - Steps state access
60
+ `useTigerConfig`, `useControlledState`, `useDrag`, `useChartInteraction`,
61
+ `useFormController`, plus the component context hooks `useFormContext`,
62
+ `useMenuContext`, `useTabsContext`, `useCollapseContext`, `useAnchorContext`,
63
+ `useBreadcrumbContext` and `useStepsContext`. Signatures are listed in
64
+ [api-summary.md](https://github.com/expcat/Tigercat/blob/main/skills/tigercat/references/shared/api-summary.md).
56
65
 
57
66
  ## License
58
67
 
@@ -0,0 +1,181 @@
1
+ import {
2
+ useFormItemControlContext
3
+ } from "./chunk-F6T3OLTQ.mjs";
4
+ import {
5
+ useControlledState
6
+ } from "./chunk-MEFARVQI.mjs";
7
+
8
+ // src/components/MaskInput.tsx
9
+ import { useEffect, useId, useLayoutEffect, useMemo, useRef } from "react";
10
+ import {
11
+ applyMaskInput,
12
+ classNames,
13
+ formatMaskValue,
14
+ getInputClasses,
15
+ getInputClearButtonClasses,
16
+ getInputErrorClasses,
17
+ getInputWrapperClasses,
18
+ injectShakeStyle,
19
+ parseMask,
20
+ SHAKE_CLASS
21
+ } from "@expcat/tigercat-core";
22
+ import { jsx, jsxs } from "react/jsx-runtime";
23
+ var MaskInput = ({
24
+ size,
25
+ status: statusProp,
26
+ errorMessage: errorMessageProp,
27
+ _shakeTrigger: shakeTriggerProp,
28
+ mask,
29
+ tokens,
30
+ value,
31
+ defaultValue,
32
+ placeholder = "",
33
+ disabled = false,
34
+ readonly = false,
35
+ clearable = false,
36
+ name,
37
+ id,
38
+ autoComplete,
39
+ autoFocus = false,
40
+ onChange,
41
+ onComplete,
42
+ onFocus,
43
+ onBlur,
44
+ onClear,
45
+ className,
46
+ style,
47
+ ...rest
48
+ }) => {
49
+ injectShakeStyle();
50
+ const formItemControl = useFormItemControlContext();
51
+ const effectiveSize = size ?? "md";
52
+ const status = statusProp ?? formItemControl?.status ?? "default";
53
+ const errorMessage = errorMessageProp ?? formItemControl?.errorMessage;
54
+ const shakeTrigger = shakeTriggerProp ?? formItemControl?.shakeTrigger;
55
+ const wrapperRef = useRef(null);
56
+ const inputRef = useRef(null);
57
+ const isComposing = useRef(false);
58
+ const pendingCaret = useRef(null);
59
+ const reactId = useId();
60
+ const errorMsgId = `tiger-mask-input-error-${reactId}`;
61
+ const spec = useMemo(() => parseMask(mask, tokens), [mask, tokens]);
62
+ const [rawValue, setRawValue] = useControlledState(value, defaultValue ?? "");
63
+ const formatted = useMemo(() => formatMaskValue(rawValue, spec), [rawValue, spec]);
64
+ const maskedValue = formatted.maskedValue;
65
+ useEffect(() => {
66
+ if (status === "error" && wrapperRef.current) {
67
+ const el = wrapperRef.current;
68
+ el.classList.remove(SHAKE_CLASS);
69
+ void el.offsetWidth;
70
+ el.classList.add(SHAKE_CLASS);
71
+ }
72
+ }, [status, shakeTrigger]);
73
+ useLayoutEffect(() => {
74
+ if (pendingCaret.current !== null && inputRef.current) {
75
+ const caret = pendingCaret.current;
76
+ inputRef.current.setSelectionRange(caret, caret);
77
+ pendingCaret.current = null;
78
+ }
79
+ });
80
+ const commit = (raw, detail, previousMasked) => {
81
+ setRawValue(raw);
82
+ onChange?.(raw, detail);
83
+ if (detail.completed && !formatMaskValue(previousMasked, spec).completed) {
84
+ onComplete?.(raw, detail.maskedValue);
85
+ }
86
+ };
87
+ const applyValue = (inputValue, caret) => {
88
+ const previousMasked = maskedValue;
89
+ const result = applyMaskInput(inputValue, caret, spec, previousMasked);
90
+ if (inputRef.current) inputRef.current.value = result.maskedValue;
91
+ pendingCaret.current = result.caret;
92
+ commit(
93
+ result.rawValue,
94
+ { maskedValue: result.maskedValue, completed: result.completed },
95
+ previousMasked
96
+ );
97
+ };
98
+ const handleInput = (event) => {
99
+ if (isComposing.current) return;
100
+ const target = event.currentTarget;
101
+ applyValue(target.value, target.selectionStart ?? target.value.length);
102
+ };
103
+ const handlePaste = (event) => {
104
+ if (disabled || readonly) return;
105
+ event.preventDefault();
106
+ const input = event.currentTarget;
107
+ const text = event.clipboardData.getData("text");
108
+ const start = input.selectionStart ?? input.value.length;
109
+ const end = input.selectionEnd ?? input.value.length;
110
+ applyValue(input.value.slice(0, start) + text + input.value.slice(end), start + text.length);
111
+ };
112
+ const handleCompositionStart = () => {
113
+ isComposing.current = true;
114
+ };
115
+ const handleCompositionEnd = (event) => {
116
+ isComposing.current = false;
117
+ const target = event.currentTarget;
118
+ applyValue(target.value, target.selectionStart ?? target.value.length);
119
+ };
120
+ const handleClear = () => {
121
+ setRawValue("");
122
+ onChange?.("", { maskedValue: "", completed: false });
123
+ onClear?.();
124
+ inputRef.current?.focus();
125
+ };
126
+ const hasSuffix = clearable;
127
+ const activeError = status === "error" && !!errorMessage;
128
+ const showClear = clearable && !disabled && !readonly && rawValue.length > 0;
129
+ const inputClasses = getInputClasses({
130
+ size: effectiveSize,
131
+ status,
132
+ hasPrefix: false,
133
+ hasSuffix
134
+ });
135
+ return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: classNames(getInputWrapperClasses(), className), style, children: [
136
+ /* @__PURE__ */ jsx(
137
+ "input",
138
+ {
139
+ ...rest,
140
+ ref: inputRef,
141
+ className: inputClasses,
142
+ type: "text",
143
+ value: maskedValue,
144
+ placeholder,
145
+ disabled,
146
+ readOnly: readonly,
147
+ id,
148
+ autoComplete,
149
+ autoFocus,
150
+ ...status === "error" ? { "aria-invalid": true } : {},
151
+ ...activeError ? { "aria-describedby": errorMsgId } : {},
152
+ onInput: handleInput,
153
+ onChange: () => {
154
+ },
155
+ onPaste: handlePaste,
156
+ onCompositionStart: handleCompositionStart,
157
+ onCompositionEnd: handleCompositionEnd,
158
+ onFocus,
159
+ onBlur
160
+ }
161
+ ),
162
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: rawValue }) : null,
163
+ activeError ? /* @__PURE__ */ jsx("div", { id: errorMsgId, className: getInputErrorClasses(effectiveSize), "aria-live": "polite", children: errorMessage }) : showClear && /* @__PURE__ */ jsx(
164
+ "button",
165
+ {
166
+ type: "button",
167
+ className: getInputClearButtonClasses(effectiveSize),
168
+ onClick: handleClear,
169
+ "aria-label": "Clear input",
170
+ tabIndex: -1,
171
+ children: "\u2715"
172
+ }
173
+ )
174
+ ] });
175
+ };
176
+ var MaskInput_default = MaskInput;
177
+
178
+ export {
179
+ MaskInput,
180
+ MaskInput_default
181
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TaskBoard
3
- } from "./chunk-773LH7BK.mjs";
3
+ } from "./chunk-O6VYD4E4.mjs";
4
4
 
5
5
  // src/components/Kanban.tsx
6
6
  import { jsx } from "react/jsx-runtime";
@@ -0,0 +1,428 @@
1
+ import {
2
+ ChartCanvas
3
+ } from "./chunk-CUAXRBKO.mjs";
4
+
5
+ // src/components/Gantt.tsx
6
+ import { useEffect, useMemo, useRef, useState } from "react";
7
+ import {
8
+ applyGanttTaskDateOverlay,
9
+ classNames,
10
+ clampGanttDragDeltaX,
11
+ computeGanttLayout,
12
+ ganttAxisTextClasses,
13
+ ganttDateValuesEqual,
14
+ ganttDependencyClasses,
15
+ ganttLabelClasses,
16
+ ganttProgressClasses,
17
+ ganttRowClasses,
18
+ ganttTodayLineClasses,
19
+ getChartInnerRect,
20
+ getGanttTaskAriaLabel,
21
+ getGanttTaskClasses,
22
+ isBrowser,
23
+ moveGanttTaskByPx
24
+ } from "@expcat/tigercat-core";
25
+ import { jsx, jsxs } from "react/jsx-runtime";
26
+ var GANTT_BAR_CLICK_PX = 4;
27
+ function Gantt({
28
+ data,
29
+ width = 760,
30
+ height = 360,
31
+ padding = 24,
32
+ scale = "week",
33
+ rowHeight = 40,
34
+ barHeight = 18,
35
+ taskLabelWidth = 140,
36
+ timelineHeight = 36,
37
+ minDate,
38
+ maxDate,
39
+ minBarWidth = 6,
40
+ showToday = false,
41
+ showProgress = true,
42
+ showDependencies = true,
43
+ hoverable = false,
44
+ selectable = false,
45
+ selectedId,
46
+ activeOpacity = 1,
47
+ inactiveOpacity = 0.35,
48
+ dateFormatter,
49
+ colors,
50
+ title,
51
+ desc,
52
+ ariaLabel = "Gantt chart",
53
+ className,
54
+ onTaskClick,
55
+ onTaskHover,
56
+ onSelectedIdChange,
57
+ onTaskChange,
58
+ onDataChange
59
+ }) {
60
+ const [innerSelectedId, setInnerSelectedId] = useState(null);
61
+ const [hoveredId, setHoveredId] = useState(null);
62
+ const [dateOverlay, setDateOverlay] = useState(
63
+ () => /* @__PURE__ */ new Map()
64
+ );
65
+ const [dragPreview, setDragPreview] = useState(
66
+ null
67
+ );
68
+ const dragSessionRef = useRef(null);
69
+ const documentPointerListeningRef = useRef(false);
70
+ const suppressClickRef = useRef(false);
71
+ const dragFnsRef = useRef({
72
+ move: (_event) => {
73
+ },
74
+ finish: (_event) => {
75
+ }
76
+ });
77
+ const onDocumentPointerMove = useRef((event) => {
78
+ dragFnsRef.current.move(event);
79
+ }).current;
80
+ const onDocumentPointerEnd = useRef((event) => {
81
+ dragFnsRef.current.finish(event);
82
+ }).current;
83
+ const dataRef = useRef(data);
84
+ dataRef.current = data;
85
+ const dateOverlayRef = useRef(dateOverlay);
86
+ dateOverlayRef.current = dateOverlay;
87
+ const onTaskChangeRef = useRef(onTaskChange);
88
+ onTaskChangeRef.current = onTaskChange;
89
+ const onDataChangeRef = useRef(onDataChange);
90
+ onDataChangeRef.current = onDataChange;
91
+ const onTaskClickRef = useRef(onTaskClick);
92
+ onTaskClickRef.current = onTaskClick;
93
+ const onSelectedIdChangeRef = useRef(onSelectedIdChange);
94
+ onSelectedIdChangeRef.current = onSelectedIdChange;
95
+ const selectedIdRef = useRef(selectedId);
96
+ selectedIdRef.current = selectedId;
97
+ const selectableRef = useRef(selectable);
98
+ selectableRef.current = selectable;
99
+ const resolvedSelectedId = selectedId === void 0 ? innerSelectedId : selectedId;
100
+ const resolvedSelectedIdRef = useRef(resolvedSelectedId);
101
+ resolvedSelectedIdRef.current = resolvedSelectedId;
102
+ useEffect(
103
+ () => () => {
104
+ if (!documentPointerListeningRef.current || !isBrowser()) return;
105
+ document.removeEventListener("pointermove", onDocumentPointerMove);
106
+ document.removeEventListener("pointerup", onDocumentPointerEnd);
107
+ document.removeEventListener("pointercancel", onDocumentPointerEnd);
108
+ documentPointerListeningRef.current = false;
109
+ },
110
+ [onDocumentPointerEnd, onDocumentPointerMove]
111
+ );
112
+ const resolvedData = useMemo(
113
+ () => applyGanttTaskDateOverlay(data, dateOverlay),
114
+ [data, dateOverlay]
115
+ );
116
+ const innerRect = useMemo(
117
+ () => getChartInnerRect(width, height, padding),
118
+ [height, padding, width]
119
+ );
120
+ const layout = useMemo(
121
+ () => computeGanttLayout(resolvedData, {
122
+ width: innerRect.width,
123
+ rowHeight,
124
+ barHeight,
125
+ taskLabelWidth,
126
+ timelineHeight,
127
+ minDate,
128
+ maxDate,
129
+ minBarWidth,
130
+ scale,
131
+ colors,
132
+ today: showToday ? /* @__PURE__ */ new Date() : void 0,
133
+ dateFormatter
134
+ }),
135
+ [
136
+ barHeight,
137
+ colors,
138
+ dateFormatter,
139
+ innerRect.width,
140
+ maxDate,
141
+ minBarWidth,
142
+ minDate,
143
+ resolvedData,
144
+ rowHeight,
145
+ scale,
146
+ showToday,
147
+ taskLabelWidth,
148
+ timelineHeight
149
+ ]
150
+ );
151
+ const activeId = resolvedSelectedId ?? hoveredId;
152
+ const selectTask = (task) => {
153
+ if (selectableRef.current && !task.task.disabled) {
154
+ const nextId = resolvedSelectedIdRef.current === task.id ? null : task.id;
155
+ if (selectedIdRef.current === void 0) setInnerSelectedId(nextId);
156
+ onSelectedIdChangeRef.current?.(nextId);
157
+ }
158
+ onTaskClickRef.current?.(task.task);
159
+ };
160
+ const setHoveredTask = (task) => {
161
+ if (!hoverable) return;
162
+ setHoveredId(task?.id ?? null);
163
+ onTaskHover?.(task?.task ?? null);
164
+ };
165
+ const getTaskOpacity = (task) => {
166
+ if (activeId === null) return activeOpacity;
167
+ return activeId === task.id ? activeOpacity : inactiveOpacity;
168
+ };
169
+ const pointerScale = (currentTarget) => {
170
+ const svg = currentTarget instanceof SVGElement ? currentTarget.ownerSVGElement ?? currentTarget.closest("svg") : currentTarget instanceof Element ? currentTarget.closest("svg") : null;
171
+ const clientWidth = svg?.clientWidth ?? 0;
172
+ if (!(clientWidth > 0)) return 1;
173
+ const viewBoxWidth = svg?.viewBox?.baseVal?.width;
174
+ const resolvedViewBox = typeof viewBoxWidth === "number" && viewBoxWidth > 0 ? viewBoxWidth : width;
175
+ return resolvedViewBox / clientWidth;
176
+ };
177
+ const captureBarPointer = (target, pointerId) => {
178
+ if (!(target instanceof Element) || typeof target.setPointerCapture !== "function") return;
179
+ try {
180
+ target.setPointerCapture(pointerId);
181
+ } catch {
182
+ }
183
+ };
184
+ const releaseBarPointer = (target, pointerId) => {
185
+ if (!target || typeof target.releasePointerCapture !== "function") return;
186
+ try {
187
+ target.releasePointerCapture(pointerId);
188
+ } catch {
189
+ }
190
+ };
191
+ const moveBarDrag = (event) => {
192
+ const session = dragSessionRef.current;
193
+ if (!session || event.pointerId !== session.pointerId) return;
194
+ const rawDelta = (event.clientX - session.startClientX) * session.scale;
195
+ const deltaX = clampGanttDragDeltaX(
196
+ rawDelta,
197
+ session.barX,
198
+ session.barWidth,
199
+ session.taskLabelWidth,
200
+ session.layoutWidth
201
+ );
202
+ session.deltaX = deltaX;
203
+ setDragPreview({ id: session.task.id, deltaX });
204
+ };
205
+ const detachDocumentPointerListeners = () => {
206
+ if (!documentPointerListeningRef.current || !isBrowser()) return;
207
+ document.removeEventListener("pointermove", onDocumentPointerMove);
208
+ document.removeEventListener("pointerup", onDocumentPointerEnd);
209
+ document.removeEventListener("pointercancel", onDocumentPointerEnd);
210
+ documentPointerListeningRef.current = false;
211
+ };
212
+ const finishBarDrag = (event) => {
213
+ const session = dragSessionRef.current;
214
+ if (!session || event.pointerId !== session.pointerId) return;
215
+ moveBarDrag(event);
216
+ const deltaX = session.deltaX;
217
+ dragSessionRef.current = null;
218
+ setDragPreview(null);
219
+ detachDocumentPointerListeners();
220
+ releaseBarPointer(session.captureTarget, session.pointerId);
221
+ suppressClickRef.current = true;
222
+ if (Math.abs(deltaX) < GANTT_BAR_CLICK_PX) {
223
+ selectTask(session.task);
224
+ return;
225
+ }
226
+ const nextTask = moveGanttTaskByPx(session.sourceTask, deltaX, {
227
+ minMs: session.minMs,
228
+ maxMs: session.maxMs,
229
+ timelineWidth: session.timelineWidth
230
+ });
231
+ if (ganttDateValuesEqual(nextTask.start, session.sourceTask.start) && ganttDateValuesEqual(nextTask.end, session.sourceTask.end)) {
232
+ selectTask(session.task);
233
+ return;
234
+ }
235
+ const incoming = dataRef.current.find((item) => item.id === nextTask.id);
236
+ const previous = dateOverlayRef.current.get(nextTask.id);
237
+ const nextOverlay = new Map(dateOverlayRef.current);
238
+ nextOverlay.set(nextTask.id, {
239
+ start: nextTask.start,
240
+ end: nextTask.end,
241
+ baseStart: previous?.baseStart ?? incoming?.start ?? session.sourceTask.start,
242
+ baseEnd: previous?.baseEnd ?? incoming?.end ?? session.sourceTask.end
243
+ });
244
+ setDateOverlay(nextOverlay);
245
+ const nextData = applyGanttTaskDateOverlay(dataRef.current, nextOverlay);
246
+ onTaskChangeRef.current?.(nextTask);
247
+ onDataChangeRef.current?.(nextData);
248
+ };
249
+ dragFnsRef.current.move = moveBarDrag;
250
+ dragFnsRef.current.finish = finishBarDrag;
251
+ const attachDocumentPointerListeners = () => {
252
+ if (documentPointerListeningRef.current || !isBrowser()) return;
253
+ document.addEventListener("pointermove", onDocumentPointerMove);
254
+ document.addEventListener("pointerup", onDocumentPointerEnd);
255
+ document.addEventListener("pointercancel", onDocumentPointerEnd);
256
+ documentPointerListeningRef.current = true;
257
+ };
258
+ const startBarDrag = (event, task) => {
259
+ if (task.task.disabled || dragSessionRef.current) return;
260
+ if (event.button !== void 0 && event.button !== 0) return;
261
+ event.preventDefault();
262
+ const captureTarget = event.currentTarget instanceof Element ? event.currentTarget : null;
263
+ dragSessionRef.current = {
264
+ pointerId: event.pointerId,
265
+ startClientX: event.clientX,
266
+ scale: pointerScale(event.currentTarget),
267
+ task,
268
+ sourceTask: task.task,
269
+ barX: task.x,
270
+ barWidth: task.width,
271
+ taskLabelWidth,
272
+ layoutWidth: layout.width,
273
+ minMs: layout.minMs,
274
+ maxMs: layout.maxMs,
275
+ timelineWidth: layout.timelineWidth,
276
+ deltaX: 0,
277
+ captureTarget
278
+ };
279
+ setDragPreview({ id: task.id, deltaX: 0 });
280
+ captureBarPointer(event.currentTarget, event.pointerId);
281
+ attachDocumentPointerListeners();
282
+ };
283
+ const handleBarClick = (event, task) => {
284
+ if (suppressClickRef.current) {
285
+ suppressClickRef.current = false;
286
+ event.preventDefault();
287
+ event.stopPropagation();
288
+ return;
289
+ }
290
+ selectTask(task);
291
+ };
292
+ return /* @__PURE__ */ jsx(
293
+ ChartCanvas,
294
+ {
295
+ width,
296
+ height,
297
+ padding,
298
+ title,
299
+ desc,
300
+ className: classNames(className),
301
+ role: "img",
302
+ "aria-label": ariaLabel,
303
+ children: /* @__PURE__ */ jsxs("g", { "data-series-type": "gantt", children: [
304
+ /* @__PURE__ */ jsxs("g", { "data-gantt-axis": "true", children: [
305
+ /* @__PURE__ */ jsx(
306
+ "line",
307
+ {
308
+ x1: taskLabelWidth,
309
+ x2: layout.width,
310
+ y1: timelineHeight - 1,
311
+ y2: timelineHeight - 1,
312
+ stroke: "var(--tiger-border,#d1d5db)"
313
+ }
314
+ ),
315
+ layout.ticks.map((tick) => /* @__PURE__ */ jsxs("g", { children: [
316
+ /* @__PURE__ */ jsx(
317
+ "line",
318
+ {
319
+ x1: tick.x,
320
+ x2: tick.x,
321
+ y1: 0,
322
+ y2: layout.height,
323
+ stroke: "var(--tiger-border,#e5e7eb)"
324
+ }
325
+ ),
326
+ /* @__PURE__ */ jsx("text", { x: tick.x + 4, y: 16, className: ganttAxisTextClasses, children: tick.label })
327
+ ] }, `${tick.label}-${tick.x}`))
328
+ ] }),
329
+ /* @__PURE__ */ jsx("g", { "data-gantt-rows": "true", children: layout.tasks.map((task) => /* @__PURE__ */ jsx(
330
+ "rect",
331
+ {
332
+ x: 0,
333
+ y: timelineHeight + task.index * rowHeight,
334
+ width: layout.width,
335
+ height: rowHeight,
336
+ className: task.index % 2 === 0 ? ganttRowClasses : void 0,
337
+ opacity: task.index % 2 === 0 ? 0.75 : 0
338
+ },
339
+ `row-${task.id}`
340
+ )) }),
341
+ showToday && layout.todayX !== null ? /* @__PURE__ */ jsx(
342
+ "line",
343
+ {
344
+ x1: layout.todayX,
345
+ x2: layout.todayX,
346
+ y1: 0,
347
+ y2: layout.height,
348
+ className: ganttTodayLineClasses,
349
+ "data-gantt-today": "true"
350
+ }
351
+ ) : null,
352
+ showDependencies ? /* @__PURE__ */ jsx("g", { "data-gantt-dependencies": "true", children: layout.dependencies.map((dependency) => /* @__PURE__ */ jsx(
353
+ "path",
354
+ {
355
+ d: dependency.path,
356
+ className: ganttDependencyClasses
357
+ },
358
+ `${dependency.sourceId}-${dependency.targetId}`
359
+ )) }) : null,
360
+ /* @__PURE__ */ jsx("g", { "data-gantt-tasks": "true", children: layout.tasks.map((task) => {
361
+ const selected = resolvedSelectedId === task.id;
362
+ const interactive = (hoverable || selectable) && !task.task.disabled;
363
+ const movable = !task.task.disabled;
364
+ const grabbing = dragPreview?.id === task.id;
365
+ const previewDeltaX = grabbing ? dragPreview?.deltaX ?? 0 : 0;
366
+ return /* @__PURE__ */ jsxs("g", { opacity: getTaskOpacity(task), children: [
367
+ /* @__PURE__ */ jsx("text", { x: 0, y: task.y + task.height / 2 + 4, className: ganttLabelClasses, children: task.task.label }),
368
+ /* @__PURE__ */ jsxs(
369
+ "g",
370
+ {
371
+ className: getGanttTaskClasses(interactive, selected, movable, grabbing),
372
+ role: interactive ? "button" : "group",
373
+ tabIndex: interactive ? 0 : void 0,
374
+ "aria-label": getGanttTaskAriaLabel(task.task),
375
+ "data-gantt-task-id": task.id,
376
+ transform: previewDeltaX !== 0 ? `translate(${previewDeltaX} 0)` : void 0,
377
+ onMouseEnter: () => setHoveredTask(task),
378
+ onMouseLeave: () => setHoveredTask(null),
379
+ onPointerDown: (event) => startBarDrag(event, task),
380
+ onPointerMove: (event) => moveBarDrag(event.nativeEvent),
381
+ onPointerUp: (event) => finishBarDrag(event.nativeEvent),
382
+ onPointerCancel: (event) => finishBarDrag(event.nativeEvent),
383
+ onClick: (event) => handleBarClick(event, task),
384
+ onKeyDown: (event) => {
385
+ if (event.key === "Enter" || event.key === " ") {
386
+ event.preventDefault();
387
+ selectTask(task);
388
+ }
389
+ },
390
+ children: [
391
+ /* @__PURE__ */ jsx(
392
+ "rect",
393
+ {
394
+ x: task.x,
395
+ y: task.y,
396
+ width: task.width,
397
+ height: task.height,
398
+ rx: 4,
399
+ fill: task.color,
400
+ stroke: selected ? "var(--tiger-text,#111827)" : void 0,
401
+ strokeWidth: selected ? 2 : void 0
402
+ }
403
+ ),
404
+ showProgress && task.progressWidth > 0 ? /* @__PURE__ */ jsx(
405
+ "rect",
406
+ {
407
+ x: task.x,
408
+ y: task.y,
409
+ width: task.progressWidth,
410
+ height: task.height,
411
+ rx: 4,
412
+ className: ganttProgressClasses
413
+ }
414
+ ) : null
415
+ ]
416
+ }
417
+ )
418
+ ] }, task.id);
419
+ }) })
420
+ ] })
421
+ }
422
+ );
423
+ }
424
+ Gantt.displayName = "Gantt";
425
+
426
+ export {
427
+ Gantt
428
+ };