@heymantle/litho 0.0.13 → 0.0.15

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 (63) hide show
  1. package/README.md +52 -0
  2. package/dist/cjs/components/Card.js +1 -1
  3. package/dist/cjs/components/Disclosure.js +46 -15
  4. package/dist/cjs/components/DropZone.js +93 -37
  5. package/dist/cjs/components/Layout.js +4 -2
  6. package/dist/cjs/components/Modal.js +14 -3
  7. package/dist/cjs/components/Navigation.js +4 -3
  8. package/dist/cjs/components/Popover.js +58 -13
  9. package/dist/cjs/components/Select.js +4 -0
  10. package/dist/cjs/components/TabNavigation.js +65 -11
  11. package/dist/cjs/components/Table.js +27 -11
  12. package/dist/cjs/components/Tabs.js +33 -2
  13. package/dist/cjs/components/TextField.js +4 -2
  14. package/dist/cjs/components/ToastNotification.js +368 -0
  15. package/dist/cjs/components/ToastProvider.js +342 -0
  16. package/dist/cjs/index.js +11 -0
  17. package/dist/cjs/playwright.config.js +114 -0
  18. package/dist/cjs/styles/Table.js +2 -7
  19. package/dist/cjs/tests/visual/stories.spec.js +637 -0
  20. package/dist/cjs/utilities/dates.js +7 -7
  21. package/dist/esm/components/Card.js +1 -1
  22. package/dist/esm/components/Disclosure.js +36 -5
  23. package/dist/esm/components/DropZone.js +94 -38
  24. package/dist/esm/components/Layout.js +4 -2
  25. package/dist/esm/components/Modal.js +14 -3
  26. package/dist/esm/components/Navigation.js +4 -3
  27. package/dist/esm/components/Popover.js +58 -13
  28. package/dist/esm/components/Select.js +5 -1
  29. package/dist/esm/components/TabNavigation.js +65 -11
  30. package/dist/esm/components/Table.js +27 -11
  31. package/dist/esm/components/Tabs.js +33 -2
  32. package/dist/esm/components/TextField.js +4 -2
  33. package/dist/esm/components/ToastNotification.js +353 -0
  34. package/dist/esm/components/ToastProvider.js +336 -0
  35. package/dist/esm/index.js +2 -0
  36. package/dist/esm/playwright.config.js +104 -0
  37. package/dist/esm/styles/Table.js +2 -7
  38. package/dist/esm/tests/visual/stories.spec.js +633 -0
  39. package/dist/esm/utilities/dates.js +7 -7
  40. package/dist/types/components/Disclosure.d.ts.map +1 -1
  41. package/dist/types/components/DropZone.d.ts +2 -0
  42. package/dist/types/components/DropZone.d.ts.map +1 -1
  43. package/dist/types/components/Layout.d.ts.map +1 -1
  44. package/dist/types/components/Modal.d.ts.map +1 -1
  45. package/dist/types/components/Navigation.d.ts +1 -0
  46. package/dist/types/components/Navigation.d.ts.map +1 -1
  47. package/dist/types/components/Popover.d.ts +2 -0
  48. package/dist/types/components/Popover.d.ts.map +1 -1
  49. package/dist/types/components/Select.d.ts.map +1 -1
  50. package/dist/types/components/TabNavigation.d.ts.map +1 -1
  51. package/dist/types/components/Table.d.ts.map +1 -1
  52. package/dist/types/components/Tabs.d.ts +45 -1
  53. package/dist/types/components/Tabs.d.ts.map +1 -1
  54. package/dist/types/components/TextField.d.ts +2 -0
  55. package/dist/types/components/TextField.d.ts.map +1 -1
  56. package/dist/types/components/ToastNotification.d.ts +36 -0
  57. package/dist/types/components/ToastNotification.d.ts.map +1 -0
  58. package/dist/types/components/ToastProvider.d.ts +21 -0
  59. package/dist/types/components/ToastProvider.d.ts.map +1 -0
  60. package/dist/types/index.d.ts +2 -0
  61. package/dist/types/styles/Table.d.ts.map +1 -1
  62. package/index.css +3 -0
  63. package/package.json +12 -3
@@ -0,0 +1,368 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "default", {
7
+ enumerable: true,
8
+ get: function() {
9
+ return _default;
10
+ }
11
+ });
12
+ var _jsxruntime = require("react/jsx-runtime");
13
+ var _react = require("react");
14
+ var _tailwindvariants = require("tailwind-variants");
15
+ var _Text = /*#__PURE__*/ _interop_require_default(require("./Text"));
16
+ var _Icon = /*#__PURE__*/ _interop_require_default(require("./Icon"));
17
+ var _polarisicons = require("@shopify/polaris-icons");
18
+ function _array_like_to_array(arr, len) {
19
+ if (len == null || len > arr.length) len = arr.length;
20
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
21
+ return arr2;
22
+ }
23
+ function _array_with_holes(arr) {
24
+ if (Array.isArray(arr)) return arr;
25
+ }
26
+ function _interop_require_default(obj) {
27
+ return obj && obj.__esModule ? obj : {
28
+ default: obj
29
+ };
30
+ }
31
+ function _iterable_to_array_limit(arr, i) {
32
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
33
+ if (_i == null) return;
34
+ var _arr = [];
35
+ var _n = true;
36
+ var _d = false;
37
+ var _s, _e;
38
+ try {
39
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
40
+ _arr.push(_s.value);
41
+ if (i && _arr.length === i) break;
42
+ }
43
+ } catch (err) {
44
+ _d = true;
45
+ _e = err;
46
+ } finally{
47
+ try {
48
+ if (!_n && _i["return"] != null) _i["return"]();
49
+ } finally{
50
+ if (_d) throw _e;
51
+ }
52
+ }
53
+ return _arr;
54
+ }
55
+ function _non_iterable_rest() {
56
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
57
+ }
58
+ function _sliced_to_array(arr, i) {
59
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
60
+ }
61
+ function _unsupported_iterable_to_array(o, minLen) {
62
+ if (!o) return;
63
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
64
+ var n = Object.prototype.toString.call(o).slice(8, -1);
65
+ if (n === "Object" && o.constructor) n = o.constructor.name;
66
+ if (n === "Map" || n === "Set") return Array.from(n);
67
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
68
+ }
69
+ var styles = (0, _tailwindvariants.tv)({
70
+ base: "Litho-ToastNotification group/toast pointer-events-auto min-w-60 max-w-85 bg-surface-highest/95 hover:bg-surface-highest rounded-md shadow-card border border-tint-2 p-2 flex items-start gap-2 transition-all duration-300 ease-in-out",
71
+ variants: {
72
+ position: {
73
+ topRight: "",
74
+ topLeft: "",
75
+ topCenter: "",
76
+ bottomRight: "",
77
+ bottomLeft: "",
78
+ bottomCenter: ""
79
+ },
80
+ fixed: {
81
+ true: "fixed z-[9999]",
82
+ false: "relative"
83
+ },
84
+ visible: {
85
+ true: "opacity-100 translate-y-0",
86
+ false: "opacity-0"
87
+ },
88
+ clickable: {
89
+ true: "cursor-pointer",
90
+ false: ""
91
+ }
92
+ },
93
+ defaultVariants: {
94
+ position: "topRight",
95
+ visible: true,
96
+ fixed: true,
97
+ clickable: false
98
+ },
99
+ compoundVariants: [
100
+ {
101
+ position: "topRight",
102
+ fixed: true,
103
+ class: "top-4 right-4"
104
+ },
105
+ {
106
+ position: "topLeft",
107
+ fixed: true,
108
+ class: "top-4 left-4"
109
+ },
110
+ {
111
+ position: "topCenter",
112
+ fixed: true,
113
+ class: "top-4 left-1/2 -translate-x-1/2"
114
+ },
115
+ {
116
+ position: "bottomRight",
117
+ fixed: true,
118
+ class: "bottom-4 right-4"
119
+ },
120
+ {
121
+ position: "bottomLeft",
122
+ fixed: true,
123
+ class: "bottom-4 left-4"
124
+ },
125
+ {
126
+ position: "bottomCenter",
127
+ fixed: true,
128
+ class: "bottom-4 left-1/2 -translate-x-1/2"
129
+ },
130
+ {
131
+ position: "topRight",
132
+ visible: false,
133
+ class: "translate-x-full"
134
+ },
135
+ {
136
+ position: "topLeft",
137
+ visible: false,
138
+ class: "-translate-x-full"
139
+ },
140
+ {
141
+ position: "topCenter",
142
+ visible: false,
143
+ class: "-translate-y-4"
144
+ },
145
+ {
146
+ position: "bottomRight",
147
+ visible: false,
148
+ class: "translate-x-full"
149
+ },
150
+ {
151
+ position: "bottomLeft",
152
+ visible: false,
153
+ class: "-translate-x-full"
154
+ },
155
+ {
156
+ position: "bottomCenter",
157
+ visible: false,
158
+ class: "translate-y-4"
159
+ }
160
+ ]
161
+ });
162
+ var closeButtonStyles = (0, _tailwindvariants.tv)({
163
+ base: "Litho-ToastNotification__Close flex-none p-0.5 rounded-md cursor-pointer hover:bg-tint-2 active:bg-tint-3 transition-colors opacity-0 group-hover/toast:opacity-100"
164
+ });
165
+ /**
166
+ * ToastNotification component for displaying temporary notifications to users.
167
+ * Automatically dismisses after the specified duration.
168
+ *
169
+ * @component
170
+ * @param {Object} props - The component props.
171
+ * @param {React.ReactNode} [props.prefix] - Optional prefix content (image, icon, or component) to display before the title.
172
+ * @param {Object|Function} [props.icon] - Optional icon source (from @shopify/polaris-icons) to display as prefix. Used as fallback if prefix is not provided.
173
+ * @param {string} props.title - The main title text of the toast.
174
+ * @param {string} [props.subtitle] - Optional subtitle text displayed below the title.
175
+ * @param {number} [props.duration=3000] - Duration in milliseconds before the toast auto-dismisses. Defaults to 2000ms (2 seconds).
176
+ * @param {'topRight' | 'topLeft' | 'topCenter' | 'bottomRight' | 'bottomLeft' | 'bottomCenter'} [props.position='topRight'] - Position of the toast on the screen.
177
+ * @param {Function} [props.onDismiss] - Callback function called when the toast is dismissed (either automatically or manually).
178
+ * @param {Function} [props.onClick] - Callback function called when the toast is clicked. Makes the entire toast clickable with cursor-pointer.
179
+ * @param {boolean} [props.dismissible=true] - Whether the toast can be manually dismissed via the close button.
180
+ * @param {boolean} [props.fixed=true] - Whether to use fixed positioning. Set to false when used within ToastProvider.
181
+ * @param {boolean} [props.isDismissing=false] - Whether the toast is in a dismissing state. Starts the exit animation immediately.
182
+ * @param {string} [props.className] - Additional CSS classes to apply to the toast container.
183
+ * @returns {JSX.Element|null} The rendered toast notification or null if dismissed.
184
+ *
185
+ * @example
186
+ * <ToastNotification
187
+ * prefix={<Icon source={TickMinor} color="success" />}
188
+ * title="Support ticket updated"
189
+ * subtitle="Your ticket has been resolved"
190
+ * duration={3000}
191
+ * position="topRight"
192
+ * onDismiss={() => console.log('Toast dismissed')}
193
+ * />
194
+ */ function ToastNotification() {
195
+ var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
196
+ var prefix = props.prefix, icon = props.icon, title = props.title, subtitle = props.subtitle, _props_duration = props.duration, duration = _props_duration === void 0 ? 3000 : _props_duration, _props_position = props.position, position = _props_position === void 0 ? "topRight" : _props_position, onDismiss = props.onDismiss, onClick = props.onClick, _props_dismissible = props.dismissible, dismissible = _props_dismissible === void 0 ? true : _props_dismissible, _props_fixed = props.fixed, fixed = _props_fixed === void 0 ? true : _props_fixed, _props_isDismissing = props.isDismissing, isDismissing = _props_isDismissing === void 0 ? false : _props_isDismissing, className = props.className;
197
+ var _useState = _sliced_to_array((0, _react.useState)(!isDismissing), 2), visible = _useState[0], setVisible = _useState[1];
198
+ var _useState1 = _sliced_to_array((0, _react.useState)(true), 2), shouldRender = _useState1[0], setShouldRender = _useState1[1];
199
+ var _useState2 = _sliced_to_array((0, _react.useState)(false), 2), isHovered = _useState2[0], setIsHovered = _useState2[1];
200
+ var timerRef = (0, _react.useRef)(null);
201
+ var startTimeRef = (0, _react.useRef)(null);
202
+ var remainingTimeRef = (0, _react.useRef)(duration);
203
+ var dismissStartedRef = (0, _react.useRef)(false);
204
+ // If isDismissing is true, start exit animation immediately
205
+ (0, _react.useEffect)(function() {
206
+ if (isDismissing) {
207
+ setVisible(false);
208
+ dismissStartedRef.current = true;
209
+ setTimeout(function() {
210
+ setShouldRender(false);
211
+ }, 300);
212
+ }
213
+ }, [
214
+ isDismissing
215
+ ]);
216
+ var handleDismiss = (0, _react.useCallback)(function() {
217
+ if (dismissStartedRef.current) {
218
+ return; // Already dismissing
219
+ }
220
+ dismissStartedRef.current = true;
221
+ setVisible(false);
222
+ // Wait for exit animation to complete before removing from DOM
223
+ setTimeout(function() {
224
+ setShouldRender(false);
225
+ if (onDismiss) {
226
+ onDismiss();
227
+ }
228
+ }, 300); // Match transition duration
229
+ }, [
230
+ onDismiss
231
+ ]);
232
+ // Initialize remaining time when duration changes
233
+ (0, _react.useEffect)(function() {
234
+ if (duration > 0) {
235
+ remainingTimeRef.current = duration;
236
+ }
237
+ }, [
238
+ duration
239
+ ]);
240
+ // Set up auto-dismiss timer
241
+ (0, _react.useEffect)(function() {
242
+ // Don't start timer if already dismissing, duration is 0, or hovered
243
+ if (isDismissing || duration <= 0 || isHovered || dismissStartedRef.current) {
244
+ return;
245
+ }
246
+ // Clear any existing timer
247
+ if (timerRef.current) {
248
+ clearTimeout(timerRef.current);
249
+ timerRef.current = null;
250
+ }
251
+ // Start timer with remaining time
252
+ startTimeRef.current = Date.now();
253
+ timerRef.current = setTimeout(function() {
254
+ handleDismiss();
255
+ }, remainingTimeRef.current);
256
+ return function() {
257
+ if (timerRef.current) {
258
+ clearTimeout(timerRef.current);
259
+ timerRef.current = null;
260
+ }
261
+ };
262
+ }, [
263
+ duration,
264
+ isDismissing,
265
+ isHovered,
266
+ handleDismiss
267
+ ]);
268
+ // Handle hover state changes
269
+ (0, _react.useEffect)(function() {
270
+ if (isHovered && timerRef.current && startTimeRef.current && !dismissStartedRef.current) {
271
+ // Pause: calculate remaining time and clear timer
272
+ var elapsed = Date.now() - startTimeRef.current;
273
+ remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
274
+ clearTimeout(timerRef.current);
275
+ timerRef.current = null;
276
+ } else if (!isHovered && !isDismissing && duration > 0 && remainingTimeRef.current > 0 && visible && !dismissStartedRef.current) {
277
+ // Resume: restart timer with remaining time
278
+ if (timerRef.current) {
279
+ clearTimeout(timerRef.current);
280
+ }
281
+ startTimeRef.current = Date.now();
282
+ timerRef.current = setTimeout(function() {
283
+ handleDismiss();
284
+ }, remainingTimeRef.current);
285
+ }
286
+ }, [
287
+ isHovered,
288
+ isDismissing,
289
+ duration,
290
+ visible,
291
+ handleDismiss
292
+ ]);
293
+ var handleMouseEnter = function() {
294
+ if (!isDismissing && visible && !dismissStartedRef.current) {
295
+ setIsHovered(true);
296
+ }
297
+ };
298
+ var handleMouseLeave = function() {
299
+ if (!isDismissing && visible && !dismissStartedRef.current) {
300
+ setIsHovered(false);
301
+ }
302
+ };
303
+ if (!shouldRender) {
304
+ return null;
305
+ }
306
+ var classes = styles({
307
+ position: position,
308
+ visible: visible,
309
+ fixed: fixed,
310
+ clickable: !!onClick
311
+ });
312
+ var closeClasses = closeButtonStyles();
313
+ var handleToastClick = function(e) {
314
+ if (onClick) {
315
+ onClick(e);
316
+ }
317
+ };
318
+ var handleCloseClick = function(e) {
319
+ e.stopPropagation(); // Prevent triggering toast onClick
320
+ handleDismiss();
321
+ };
322
+ // Determine what to render as prefix: use prefix if provided, otherwise fallback to icon
323
+ var prefixContent = prefix || (icon ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_Icon.default, {
324
+ source: icon,
325
+ color: "subdued"
326
+ }) : null);
327
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
328
+ className: "".concat(classes).concat(className ? " ".concat(className) : ""),
329
+ onClick: handleToastClick,
330
+ onMouseEnter: handleMouseEnter,
331
+ onMouseLeave: handleMouseLeave,
332
+ children: [
333
+ prefixContent && /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
334
+ className: "Litho-ToastNotification__Prefix flex-none",
335
+ children: prefixContent
336
+ }),
337
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
338
+ className: "Litho-ToastNotification__Content flex-1 flex flex-col gap-0.5 min-w-0",
339
+ children: [
340
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_Text.default, {
341
+ fontWeight: "medium",
342
+ variant: "bodySm",
343
+ clampLines: 1,
344
+ children: title
345
+ }),
346
+ subtitle && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Text.default, {
347
+ variant: "bodySm",
348
+ color: "subdued",
349
+ clampLines: 2,
350
+ children: subtitle
351
+ })
352
+ ]
353
+ }),
354
+ dismissible && /*#__PURE__*/ (0, _jsxruntime.jsx)("button", {
355
+ type: "button",
356
+ onClick: handleCloseClick,
357
+ className: closeClasses,
358
+ "aria-label": "Dismiss notification",
359
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_Icon.default, {
360
+ source: _polarisicons.CancelMinor,
361
+ size: "sm",
362
+ color: "subdued"
363
+ })
364
+ })
365
+ ]
366
+ });
367
+ }
368
+ var _default = ToastNotification;