@flikk/ui 1.0.0-beta.25 → 1.0.0-beta.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ai/CodeBlock/CodeBlock.theme.js +2 -2
- package/dist/components/ai/PromptInput/PromptInput.js +1 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +1 -0
- package/dist/components/ai/StreamingResponse/MarkdownRenderer.js +1 -1
- package/dist/components/ai/TokenCounter/TokenCounter.theme.js +1 -1
- package/dist/components/charts/BarChart/BarChart.js +2 -2
- package/dist/components/charts/StackedBarChart/StackedBarChart.js +1 -1
- package/dist/components/charts/shared/BarRenderer/BarRenderer.js +2 -2
- package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +1 -1
- package/dist/components/core/Button/Button.js +1 -1
- package/dist/components/core/Button/Button.theme.js +4 -4
- package/dist/components/core/Calendar/CalendarMini/CalendarMini.theme.js +1 -1
- package/dist/components/core/ContextMenu/ContextMenu.theme.js +1 -1
- package/dist/components/core/Drawer/Drawer.theme.js +2 -2
- package/dist/components/core/Dropdown/Dropdown.theme.js +2 -2
- package/dist/components/core/Empty/Empty.js +1 -1
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.d.ts +14 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.js +28 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.d.ts +6 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.theme.js +8 -0
- package/dist/components/core/ImagePlaceholder/ImagePlaceholder.types.d.ts +32 -0
- package/dist/components/core/ImagePlaceholder/index.d.ts +2 -0
- package/dist/components/core/Message/Message.theme.js +1 -1
- package/dist/components/core/Modal/Modal.theme.js +1 -1
- package/dist/components/core/NavItem/NavItem.d.ts +1 -1
- package/dist/components/core/NavItem/NavItem.js +171 -68
- package/dist/components/core/NavItem/NavItem.theme.js +20 -12
- package/dist/components/core/NavItem/NavItem.types.d.ts +17 -39
- package/dist/components/core/Pagination/Pagination.theme.js +3 -3
- package/dist/components/core/Popover/Popover.theme.js +1 -1
- package/dist/components/core/Progress/Progress.theme.js +1 -1
- package/dist/components/core/ScrollArea/ScrollArea.js +2 -2
- package/dist/components/core/ScrollArea/ScrollArea.theme.js +1 -1
- package/dist/components/core/Segmented/Segmented.js +8 -6
- package/dist/components/core/Segmented/Segmented.theme.js +3 -3
- package/dist/components/core/Sidebar/Sidebar.d.ts +1 -1
- package/dist/components/core/Sidebar/Sidebar.js +42 -13
- package/dist/components/core/Sidebar/Sidebar.theme.js +5 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +33 -4
- package/dist/components/core/Sidebar/SidebarContent.js +6 -6
- package/dist/components/core/Sidebar/SidebarContext.d.ts +17 -16
- package/dist/components/core/Sidebar/SidebarContext.js +42 -19
- package/dist/components/core/Sidebar/SidebarFooter.js +4 -6
- package/dist/components/core/Sidebar/SidebarHeader.js +4 -9
- package/dist/components/core/Sidebar/SidebarLogo.js +3 -7
- package/dist/components/core/Sidebar/SidebarNav.js +4 -3
- package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -7
- package/dist/components/core/Sidebar/SidebarToggle.js +6 -1
- package/dist/components/core/Sidebar/SidebarUserProfile.js +33 -17
- package/dist/components/core/Sidebar/index.d.ts +2 -3
- package/dist/components/core/Skeleton/Skeleton.theme.js +1 -1
- package/dist/components/core/Spinner/Spinner.theme.js +1 -1
- package/dist/components/core/Tabs/Tabs.js +6 -1
- package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
- package/dist/components/core/Tabs/TabsBody.js +5 -3
- package/dist/components/core/Tag/Tag.theme.js +1 -1
- package/dist/components/core/index.d.ts +1 -0
- package/dist/components/core/index.js +2 -2
- package/dist/components/data-display/GanttChart/GanttTableGroupRow.js +1 -1
- package/dist/components/data-display/KPI/KPI.js +13 -3
- package/dist/components/data-display/KPI/KPI.theme.js +3 -3
- package/dist/components/data-display/Metric/Metric.theme.js +3 -3
- package/dist/components/data-display/Table/Table.js +4 -3
- package/dist/components/data-display/Table/Table.theme.js +11 -1
- package/dist/components/data-display/Table/Table.types.d.ts +15 -0
- package/dist/components/data-display/Table/TableColumnManager.js +1 -1
- package/dist/components/data-display/Table/index.d.ts +2 -2
- package/dist/components/effects/Aurora/Aurora.js +79 -40
- package/dist/components/effects/InteractiveCharacters/InteractiveCharacters.js +3 -3
- package/dist/components/effects/InteractiveCharacters/characters/BlobCharacter.js +13 -4
- package/dist/components/effects/InteractiveCharacters/characters/SmallCharacter.js +13 -4
- package/dist/components/effects/InteractiveCharacters/characters/TallCharacter.js +14 -4
- package/dist/components/effects/MorphingText/MorphingText.js +40 -7
- package/dist/components/effects/StripePattern/StripePattern.d.ts +18 -0
- package/dist/components/effects/StripePattern/StripePattern.js +54 -0
- package/dist/components/effects/StripePattern/StripePattern.types.d.ts +53 -0
- package/dist/components/effects/StripePattern/index.d.ts +2 -0
- package/dist/components/effects/index.d.ts +2 -0
- package/dist/components/effects/index.js +1 -0
- package/dist/components/forms/Checkbox/Checkbox.theme.js +1 -1
- package/dist/components/forms/CronInput/CronInput.theme.js +16 -16
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
- package/dist/components/forms/FileUpload/FileUpload.js +1 -1
- package/dist/components/forms/FileUpload/FileUploadProgress.js +27 -20
- package/dist/components/forms/FileUpload/FileUploadProgress.theme.js +2 -2
- package/dist/components/forms/Input/Input.theme.js +9 -11
- package/dist/components/forms/Mention/Mention.theme.js +3 -3
- package/dist/components/forms/Radio/Radio.theme.js +1 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.d.ts +18 -0
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +1231 -236
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.d.ts +53 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +99 -25
- package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +42 -3
- package/dist/components/forms/RichTextEditor/index.d.ts +2 -2
- package/dist/components/forms/Select/Select.js +1 -1
- package/dist/components/forms/Slider/Slider.theme.js +1 -1
- package/dist/components/forms/Switch/Switch.theme.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.theme.js +2 -2
- package/dist/components/forms/TimePicker/TimePickerContent.js +1 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +12 -12
- package/dist/components/forms/index.js +1 -0
- package/dist/components/layout/FormLayout/FormLayout.js +6 -4
- package/dist/components/layout/FormLayout/FormLayout.types.d.ts +3 -3
- package/dist/components/layout/FormLayout/FormLayoutSection.js +6 -1
- package/dist/components/layout/PageLayout/PageLayout.d.ts +2 -2
- package/dist/components/layout/PageLayout/PageLayout.js +23 -5
- package/dist/components/layout/PageLayout/PageLayout.theme.d.ts +2 -1
- package/dist/components/layout/PageLayout/PageLayout.theme.js +35 -6
- package/dist/components/layout/PageLayout/PageLayout.types.d.ts +45 -0
- package/dist/components/layout/PageLayout/PageLayoutContent.js +6 -4
- package/dist/components/layout/PageLayout/PageLayoutHeader.js +3 -3
- package/dist/components/layout/PageLayout/PageLayoutSidebar.js +5 -3
- package/dist/components/layout/PageLayout/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/styles/theme.css +127 -92
- package/dist/components/core/Sidebar/SidebarSubmenu.d.ts +0 -7
- package/dist/components/core/Sidebar/SidebarSubmenu.js +0 -12
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useId } from 'react';
|
|
2
3
|
import { useTransform, motion } from 'motion/react';
|
|
3
4
|
import { eyelidVariants, clickTransition, bodyTransition, smallBodyVariants, smallMouthPaths } from '../InteractiveCharacters.animations.js';
|
|
4
5
|
|
|
5
|
-
const LEFT_EYE = { cx: -
|
|
6
|
-
const RIGHT_EYE = { cx:
|
|
6
|
+
const LEFT_EYE = { cx: -13, cy: -34 };
|
|
7
|
+
const RIGHT_EYE = { cx: 12, cy: -34 };
|
|
7
8
|
const CLICK_STATES = new Set(['surprised', 'love', 'excited']);
|
|
8
9
|
const SmallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
10
|
+
const id = useId().replace(/:/g, '');
|
|
11
|
+
const leftClipId = `${id}-glyph-eye-l`;
|
|
12
|
+
const rightClipId = `${id}-glyph-eye-r`;
|
|
13
|
+
const bodyGradientId = `${id}-glyph-body`;
|
|
9
14
|
const leftPupilX = useTransform(eyeOffset.x, (v) => LEFT_EYE.cx + v);
|
|
10
15
|
const leftPupilY = useTransform(eyeOffset.y, (v) => LEFT_EYE.cy + v);
|
|
11
16
|
const rightPupilX = useTransform(eyeOffset.x, (v) => RIGHT_EYE.cx + v);
|
|
@@ -24,13 +29,17 @@ const SmallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
|
24
29
|
x: idle.sway,
|
|
25
30
|
y: idle.bob,
|
|
26
31
|
scale: idle.breath,
|
|
27
|
-
}, onClick: onClick, children: [jsx("
|
|
32
|
+
}, onClick: onClick, children: [jsx("defs", { children: jsxs("linearGradient", { id: bodyGradientId, x1: "-48", y1: "-73", x2: "38", y2: "0", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#FDA4AF" }), jsx("stop", { offset: "1", stopColor: "#FB7185" })] }) }), jsx(motion.path, { d: "M -38 -28 C -43 -57 -18 -78 13 -67 C 39 -58 50 -27 31 -8 C 13 10 -29 3 -38 -28 Z", fill: `url(#${bodyGradientId})`, animate: {
|
|
33
|
+
d: state === 'excited'
|
|
34
|
+
? 'M -40 -31 C -47 -63 -17 -85 17 -72 C 45 -61 56 -27 33 -5 C 12 15 -31 5 -40 -31 Z'
|
|
35
|
+
: 'M -38 -28 C -43 -57 -18 -78 13 -67 C 39 -58 50 -27 31 -8 C 13 10 -29 3 -38 -28 Z',
|
|
36
|
+
}, transition: transition }), jsx("path", { d: "M 20 -59 C 38 -47 42 -26 31 -13", fill: "none", stroke: "#FFE4E6", strokeWidth: 4, strokeLinecap: "round", opacity: 0.5 }), jsx("circle", { cx: -44, cy: -61, r: 7, fill: "#38BDF8", opacity: state === 'excited' ? 0.95 : 0.65 }), jsxs(motion.g, { style: {
|
|
28
37
|
originX: '0px',
|
|
29
38
|
originY: '-30px',
|
|
30
39
|
rotate: combinedHeadRotate,
|
|
31
40
|
x: combinedHeadX,
|
|
32
41
|
y: combinedHeadY,
|
|
33
|
-
}, children: [jsxs("g", { children: [jsx("clipPath", { id:
|
|
42
|
+
}, children: [jsxs("g", { children: [jsx("clipPath", { id: leftClipId, children: jsx("circle", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, r: 10 }) }), jsx("circle", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, r: 8.5, fill: "#FFF7ED" }), jsx(motion.circle, { cx: leftPupilX, cy: leftPupilY, r: 5.5, fill: "#253040", clipPath: `url(#${leftClipId})` }), jsx("circle", { cx: LEFT_EYE.cx + 3, cy: LEFT_EYE.cy - 4, r: 2.2, fill: "white", opacity: 0.95 }), jsx("circle", { cx: LEFT_EYE.cx - 2, cy: LEFT_EYE.cy + 3, r: 1.1, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: LEFT_EYE.cx - 8.5, y: LEFT_EYE.cy - 8.5, width: 17, height: 17, fill: "#FB7B8C", rx: 2, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsxs("g", { children: [jsx("clipPath", { id: rightClipId, children: jsx("circle", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, r: 10 }) }), jsx("circle", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, r: 8.5, fill: "#FFF7ED" }), jsx(motion.circle, { cx: rightPupilX, cy: rightPupilY, r: 5.5, fill: "#253040", clipPath: `url(#${rightClipId})` }), jsx("circle", { cx: RIGHT_EYE.cx + 3, cy: RIGHT_EYE.cy - 4, r: 2.2, fill: "white", opacity: 0.95 }), jsx("circle", { cx: RIGHT_EYE.cx - 2, cy: RIGHT_EYE.cy + 3, r: 1.1, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: RIGHT_EYE.cx - 8.5, y: RIGHT_EYE.cy - 8.5, width: 17, height: 17, fill: "#FB7B8C", rx: 2, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsx("ellipse", { cx: -25, cy: -20, rx: 5, ry: 3, fill: "#FFE4E6", opacity: state === 'excited' ? 0.9 : 0.55 }), jsx("ellipse", { cx: 23, cy: -20, rx: 5, ry: 3, fill: "#FFE4E6", opacity: state === 'excited' ? 0.9 : 0.55 }), jsx(motion.path, { d: smallMouthPaths[state], fill: "none", stroke: "#1E293B", strokeWidth: 2, strokeLinecap: "round", animate: { d: smallMouthPaths[state] }, transition: transition, transform: "translate(0, -17)" })] })] }));
|
|
34
43
|
};
|
|
35
44
|
|
|
36
45
|
export { SmallCharacter };
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useId } from 'react';
|
|
2
3
|
import { useTransform, motion } from 'motion/react';
|
|
3
4
|
import { eyelidVariants, clickTransition, bodyTransition, tallBodyVariants, eyebrowVariants, tallMouthPaths } from '../InteractiveCharacters.animations.js';
|
|
4
5
|
|
|
5
|
-
const LEFT_EYE = { cx: -12, cy: -
|
|
6
|
-
const RIGHT_EYE = { cx: 12, cy: -
|
|
6
|
+
const LEFT_EYE = { cx: -12, cy: -73 };
|
|
7
|
+
const RIGHT_EYE = { cx: 12, cy: -73 };
|
|
7
8
|
const CLICK_STATES = new Set(['surprised', 'love', 'excited']);
|
|
8
9
|
const TallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
10
|
+
const id = useId().replace(/:/g, '');
|
|
11
|
+
const leftClipId = `${id}-stack-eye-l`;
|
|
12
|
+
const rightClipId = `${id}-stack-eye-r`;
|
|
13
|
+
const topGradientId = `${id}-stack-top`;
|
|
14
|
+
const baseGradientId = `${id}-stack-base`;
|
|
9
15
|
const leftPupilX = useTransform(eyeOffset.x, (v) => LEFT_EYE.cx + v);
|
|
10
16
|
const leftPupilY = useTransform(eyeOffset.y, (v) => LEFT_EYE.cy + v);
|
|
11
17
|
const rightPupilX = useTransform(eyeOffset.x, (v) => RIGHT_EYE.cx + v);
|
|
@@ -25,13 +31,17 @@ const TallCharacter = ({ state, eyeOffset, idle, blinkAmount, onClick, }) => {
|
|
|
25
31
|
x: idle.sway,
|
|
26
32
|
y: idle.bob,
|
|
27
33
|
scale: idle.breath,
|
|
28
|
-
}, onClick: onClick, children: [
|
|
34
|
+
}, onClick: onClick, children: [jsxs("defs", { children: [jsxs("linearGradient", { id: topGradientId, x1: "-33", y1: "-128", x2: "34", y2: "-54", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#A5B4FC" }), jsx("stop", { offset: "1", stopColor: "#6366F1" })] }), jsxs("linearGradient", { id: baseGradientId, x1: "-46", y1: "-69", x2: "45", y2: "0", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#67E8F9" }), jsx("stop", { offset: "1", stopColor: "#14B8A6" })] })] }), jsx(motion.path, { d: "M -20 -132 C 8 -146 37 -124 34 -94 C 32 -70 8 -57 -16 -65 C -42 -74 -47 -117 -20 -132 Z", fill: `url(#${topGradientId})`, animate: {
|
|
35
|
+
d: state === 'love'
|
|
36
|
+
? 'M -22 -136 C 9 -153 42 -126 37 -92 C 34 -66 8 -53 -18 -63 C -48 -74 -51 -120 -22 -136 Z'
|
|
37
|
+
: 'M -20 -132 C 8 -146 37 -124 34 -94 C 32 -70 8 -57 -16 -65 C -42 -74 -47 -117 -20 -132 Z',
|
|
38
|
+
}, transition: transition }), jsx("path", { d: "M -39 -63 C -15 -86 31 -78 43 -47 C 55 -13 28 9 -10 4 C -47 -1 -66 -35 -39 -63 Z", fill: `url(#${baseGradientId})` }), jsx("path", { d: "M -25 -121 C -6 -132 15 -129 25 -112", fill: "none", stroke: "#E0E7FF", strokeLinecap: "round", strokeWidth: 4, opacity: 0.48 }), jsxs(motion.g, { style: {
|
|
29
39
|
originX: '0px',
|
|
30
40
|
originY: '-70px',
|
|
31
41
|
rotate: combinedHeadRotate,
|
|
32
42
|
x: combinedHeadX,
|
|
33
43
|
y: combinedHeadY,
|
|
34
|
-
}, children: [jsx(motion.line, { x1: -
|
|
44
|
+
}, children: [jsx(motion.line, { x1: -23, y1: -94, x2: -5, y2: -94, stroke: "#1E293B", strokeWidth: 2.5, strokeLinecap: "round", animate: { rotate: eyebrow.rotate, y: eyebrow.y }, transition: transition, style: { originX: '50%', originY: '50%', transformBox: 'fill-box' } }), jsx(motion.line, { x1: 5, y1: -94, x2: 23, y2: -94, stroke: "#1E293B", strokeWidth: 2.5, strokeLinecap: "round", animate: { rotate: -eyebrow.rotate, y: eyebrow.y }, transition: transition, style: { originX: '50%', originY: '50%', transformBox: 'fill-box' } }), jsxs("g", { children: [jsx("clipPath", { id: leftClipId, children: jsx("ellipse", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, rx: 11, ry: 14 }) }), jsx("ellipse", { cx: LEFT_EYE.cx, cy: LEFT_EYE.cy, rx: 10, ry: 12, fill: "#F8FAFC" }), jsx(motion.circle, { cx: leftPupilX, cy: leftPupilY, r: 6.5, fill: "#1E293B", clipPath: `url(#${leftClipId})` }), jsx("circle", { cx: LEFT_EYE.cx + 3.5, cy: LEFT_EYE.cy - 5, r: 2.5, fill: "white", opacity: 0.95 }), jsx("circle", { cx: LEFT_EYE.cx - 2.5, cy: LEFT_EYE.cy + 3, r: 1.2, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: LEFT_EYE.cx - 10, y: LEFT_EYE.cy - 12, width: 20, height: 24, fill: "#7C83F3", rx: 3, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsxs("g", { children: [jsx("clipPath", { id: rightClipId, children: jsx("ellipse", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, rx: 11, ry: 14 }) }), jsx("ellipse", { cx: RIGHT_EYE.cx, cy: RIGHT_EYE.cy, rx: 10, ry: 12, fill: "#F8FAFC" }), jsx(motion.circle, { cx: rightPupilX, cy: rightPupilY, r: 6.5, fill: "#1E293B", clipPath: `url(#${rightClipId})` }), jsx("circle", { cx: RIGHT_EYE.cx + 3.5, cy: RIGHT_EYE.cy - 5, r: 2.5, fill: "white", opacity: 0.95 }), jsx("circle", { cx: RIGHT_EYE.cx - 2.5, cy: RIGHT_EYE.cy + 3, r: 1.2, fill: "white", opacity: 0.7 }), jsx(motion.rect, { x: RIGHT_EYE.cx - 10, y: RIGHT_EYE.cy - 12, width: 20, height: 24, fill: "#7C83F3", rx: 3, style: { scaleY: eyelidScale, originX: '50%', originY: '0%', transformBox: 'fill-box' } })] }), jsx("ellipse", { cx: -25, cy: -57, rx: 6, ry: 3.5, fill: "#C4B5FD", opacity: state === 'love' ? 0.75 : 0.45 }), jsx("ellipse", { cx: 25, cy: -57, rx: 6, ry: 3.5, fill: "#C4B5FD", opacity: state === 'love' ? 0.75 : 0.45 }), jsx(motion.path, { d: tallMouthPaths[state], fill: "none", stroke: "#1E293B", strokeWidth: 2, strokeLinecap: "round", animate: { d: tallMouthPaths[state] }, transition: transition, transform: "translate(0, -49)" })] })] }));
|
|
35
45
|
};
|
|
36
46
|
|
|
37
47
|
export { TallCharacter };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import React__default, {
|
|
3
|
-
import { useReducedMotion,
|
|
2
|
+
import React__default, { useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
|
|
3
|
+
import { useReducedMotion, motion, AnimatePresence } from 'motion/react';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
|
|
6
|
+
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
6
7
|
/**
|
|
7
8
|
* MorphingText smoothly morphs between different text strings using a blur/fade
|
|
8
9
|
* transition. Two overlapping text layers cross-fade with a Gaussian blur effect
|
|
@@ -18,11 +19,12 @@ import { cn } from '../../../utils/cn.js';
|
|
|
18
19
|
* ```
|
|
19
20
|
*/
|
|
20
21
|
const MorphingText = React__default.forwardRef(({ texts, interval = 3000, transitionDuration = 800, loop = true, pauseOnHover = false, currentIndex: controlledIndex, onTextChange, className, ...props }, ref) => {
|
|
21
|
-
const filterId = useId();
|
|
22
22
|
const shouldReduceMotion = useReducedMotion();
|
|
23
23
|
const isControlled = controlledIndex !== undefined;
|
|
24
24
|
const [internalIndex, setInternalIndex] = useState(0);
|
|
25
25
|
const activeIndex = isControlled ? controlledIndex : internalIndex;
|
|
26
|
+
const measureRefs = useRef([]);
|
|
27
|
+
const [textRects, setTextRects] = useState([]);
|
|
26
28
|
const [isPaused, setIsPaused] = useState(false);
|
|
27
29
|
const intervalRef = useRef(null);
|
|
28
30
|
const hasReachedEnd = useRef(false);
|
|
@@ -59,6 +61,28 @@ const MorphingText = React__default.forwardRef(({ texts, interval = 3000, transi
|
|
|
59
61
|
useEffect(() => {
|
|
60
62
|
hasReachedEnd.current = false;
|
|
61
63
|
}, [loop, texts]);
|
|
64
|
+
useIsomorphicLayoutEffect(() => {
|
|
65
|
+
const measure = () => {
|
|
66
|
+
setTextRects(texts.map((_, index) => {
|
|
67
|
+
var _a, _b, _c;
|
|
68
|
+
const rect = (_a = measureRefs.current[index]) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
69
|
+
return {
|
|
70
|
+
width: (_b = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _b !== void 0 ? _b : 0,
|
|
71
|
+
height: (_c = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _c !== void 0 ? _c : 0,
|
|
72
|
+
};
|
|
73
|
+
}));
|
|
74
|
+
};
|
|
75
|
+
measure();
|
|
76
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const observer = new ResizeObserver(measure);
|
|
80
|
+
measureRefs.current.forEach((element) => {
|
|
81
|
+
if (element)
|
|
82
|
+
observer.observe(element);
|
|
83
|
+
});
|
|
84
|
+
return () => observer.disconnect();
|
|
85
|
+
}, [texts]);
|
|
62
86
|
// Fire onTextChange for controlled index changes
|
|
63
87
|
useEffect(() => {
|
|
64
88
|
if (isControlled) {
|
|
@@ -78,14 +102,23 @@ const MorphingText = React__default.forwardRef(({ texts, interval = 3000, transi
|
|
|
78
102
|
}
|
|
79
103
|
const durationSeconds = transitionDuration / 1000;
|
|
80
104
|
const currentText = texts[safeIndex];
|
|
105
|
+
const currentRect = textRects[safeIndex];
|
|
106
|
+
const measuredSize = (currentRect === null || currentRect === void 0 ? void 0 : currentRect.width) && (currentRect === null || currentRect === void 0 ? void 0 : currentRect.height)
|
|
107
|
+
? { width: currentRect.width, height: currentRect.height }
|
|
108
|
+
: undefined;
|
|
81
109
|
// Reduced motion: instant swap with no animation
|
|
82
110
|
if (shouldReduceMotion) {
|
|
83
111
|
return (jsx("div", { ref: ref, className: cn('relative inline-block', className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, "aria-live": "polite", "aria-atomic": "true", ...props, children: jsx("span", { children: currentText }) }));
|
|
84
112
|
}
|
|
85
|
-
return (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
113
|
+
return (jsx("div", { ref: ref, className: cn('relative inline-block align-bottom', className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, "aria-live": "polite", "aria-atomic": "true", ...props, children: jsxs(motion.span, { className: "relative inline-block align-bottom", initial: false, animate: measuredSize, transition: {
|
|
114
|
+
duration: durationSeconds,
|
|
115
|
+
ease: 'easeInOut',
|
|
116
|
+
}, children: [jsx("span", { className: "pointer-events-none invisible absolute left-0 top-0 whitespace-nowrap", "aria-hidden": "true", children: texts.map((text, index) => (jsx("span", { ref: (element) => {
|
|
117
|
+
measureRefs.current[index] = element;
|
|
118
|
+
}, className: "block w-fit whitespace-nowrap", children: text }, `${text}-${index}`))) }), !measuredSize && (jsx("span", { className: "whitespace-nowrap", children: currentText })), measuredSize && (jsx(AnimatePresence, { mode: "popLayout", initial: false, children: jsx(motion.span, { className: "absolute left-0 top-0 flex whitespace-nowrap", initial: { opacity: 0, filter: 'blur(8px)' }, animate: { opacity: 1, filter: 'blur(0px)' }, exit: { opacity: 0, filter: 'blur(8px)' }, transition: {
|
|
119
|
+
duration: durationSeconds,
|
|
120
|
+
ease: 'easeInOut',
|
|
121
|
+
}, children: currentText }, safeIndex) }))] }) }));
|
|
89
122
|
});
|
|
90
123
|
MorphingText.displayName = "MorphingText";
|
|
91
124
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { StripePatternProps } from "./StripePattern.types";
|
|
3
|
+
/**
|
|
4
|
+
* StripePattern renders an SVG-based diagonal stripe background pattern.
|
|
5
|
+
*
|
|
6
|
+
* Use it as an absolute-positioned decorative or placeholder fill inside a
|
|
7
|
+
* container with `position: relative`. The pattern is non-interactive
|
|
8
|
+
* (`pointer-events: none`) and sits at `z-index: 0` by default.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <div className="relative overflow-hidden h-40 rounded-lg">
|
|
13
|
+
* <StripePattern />
|
|
14
|
+
* <div className="relative z-10">Empty cover slot</div>
|
|
15
|
+
* </div>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const StripePattern: React.ForwardRefExoticComponent<StripePatternProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { useId, useMemo } from 'react';
|
|
3
|
+
import { useReducedMotion, motion } from 'motion/react';
|
|
4
|
+
import { cn } from '../../../utils/cn.js';
|
|
5
|
+
|
|
6
|
+
function buildMaskImage(direction, fadeSize) {
|
|
7
|
+
const stop = Math.max(0, Math.min(100, fadeSize));
|
|
8
|
+
if (direction === "radial") {
|
|
9
|
+
return `radial-gradient(ellipse at center, black ${100 - stop}%, transparent 100%)`;
|
|
10
|
+
}
|
|
11
|
+
const directionMap = {
|
|
12
|
+
top: "to top",
|
|
13
|
+
bottom: "to bottom",
|
|
14
|
+
left: "to left",
|
|
15
|
+
right: "to right",
|
|
16
|
+
};
|
|
17
|
+
return `linear-gradient(${directionMap[direction]}, black ${100 - stop}%, transparent 100%)`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* StripePattern renders an SVG-based diagonal stripe background pattern.
|
|
21
|
+
*
|
|
22
|
+
* Use it as an absolute-positioned decorative or placeholder fill inside a
|
|
23
|
+
* container with `position: relative`. The pattern is non-interactive
|
|
24
|
+
* (`pointer-events: none`) and sits at `z-index: 0` by default.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <div className="relative overflow-hidden h-40 rounded-lg">
|
|
29
|
+
* <StripePattern />
|
|
30
|
+
* <div className="relative z-10">Empty cover slot</div>
|
|
31
|
+
* </div>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
const StripePattern = React__default.forwardRef(({ spacing = 12, strokeWidth = 6, angle = -45, color = "color-mix(in srgb, var(--color-border) 50%, transparent)", fade = false, fadeDirection = "radial", fadeSize = 50, animated = false, className, style, ...rest }, ref) => {
|
|
35
|
+
const patternId = useId();
|
|
36
|
+
const shouldReduceMotion = useReducedMotion();
|
|
37
|
+
const enableAnimation = animated && !shouldReduceMotion;
|
|
38
|
+
const maskImage = useMemo(() => (fade ? buildMaskImage(fadeDirection, fadeSize) : undefined), [fade, fadeDirection, fadeSize]);
|
|
39
|
+
const containerStyle = {
|
|
40
|
+
...style,
|
|
41
|
+
...(maskImage
|
|
42
|
+
? {
|
|
43
|
+
maskImage,
|
|
44
|
+
WebkitMaskImage: maskImage,
|
|
45
|
+
}
|
|
46
|
+
: {}),
|
|
47
|
+
};
|
|
48
|
+
const lineX = spacing / 2;
|
|
49
|
+
const lineElement = enableAnimation ? (jsx(motion.line, { x1: lineX, y1: 0, x2: lineX, y2: spacing, stroke: color, strokeWidth: strokeWidth, animate: { opacity: [0.4, 1, 0.4] }, transition: { duration: 4, ease: "easeInOut", repeat: Infinity } })) : (jsx("line", { x1: lineX, y1: 0, x2: lineX, y2: spacing, stroke: color, strokeWidth: strokeWidth }));
|
|
50
|
+
return (jsx("div", { ref: ref, className: cn("absolute inset-0 pointer-events-none z-0 overflow-hidden", className), style: containerStyle, ...rest, children: jsxs("svg", { "aria-hidden": "true", className: "absolute inset-0 w-full h-full", children: [jsx("defs", { children: jsx("pattern", { id: patternId, x: "0", y: "0", width: spacing, height: spacing, patternUnits: "userSpaceOnUse", patternTransform: `rotate(${angle})`, children: lineElement }) }), jsx("rect", { width: "100%", height: "100%", fill: `url(#${patternId})` })] }) }));
|
|
51
|
+
});
|
|
52
|
+
StripePattern.displayName = "StripePattern";
|
|
53
|
+
|
|
54
|
+
export { StripePattern };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/** Direction for the fade gradient mask */
|
|
3
|
+
export type StripePatternFadeDirection = "radial" | "top" | "bottom" | "left" | "right";
|
|
4
|
+
export interface StripePatternProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Distance between adjacent stripes in pixels (center to center).
|
|
7
|
+
* @default 12
|
|
8
|
+
*/
|
|
9
|
+
spacing?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Thickness of each stripe in pixels.
|
|
12
|
+
* @default 6
|
|
13
|
+
*/
|
|
14
|
+
strokeWidth?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Stripe rotation in degrees. `-45` runs bottom-left to top-right.
|
|
17
|
+
* @default -45
|
|
18
|
+
*/
|
|
19
|
+
angle?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Stroke color of the stripes.
|
|
22
|
+
* Accepts any valid CSS color value or CSS variable.
|
|
23
|
+
* @default "color-mix(in srgb, var(--color-border) 50%, transparent)"
|
|
24
|
+
*/
|
|
25
|
+
color?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to apply a gradient mask that fades the pattern at the edges.
|
|
28
|
+
* Defaults to `false` so the pattern reads as a flat fill — useful for
|
|
29
|
+
* placeholder surfaces. Set to `true` for decorative backgrounds.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
fade?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Direction of the fade gradient.
|
|
35
|
+
* - "radial" fades from center outward
|
|
36
|
+
* - "top" | "bottom" | "left" | "right" fades toward that edge
|
|
37
|
+
* @default "radial"
|
|
38
|
+
*/
|
|
39
|
+
fadeDirection?: StripePatternFadeDirection;
|
|
40
|
+
/**
|
|
41
|
+
* Size of the fade area as a percentage (0-100).
|
|
42
|
+
* @default 50
|
|
43
|
+
*/
|
|
44
|
+
fadeSize?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Whether to apply a subtle pulse animation to the stripes.
|
|
47
|
+
* Respects prefers-reduced-motion automatically.
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
animated?: boolean;
|
|
51
|
+
/** Additional className for style overrides (shadcn pattern). */
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
@@ -20,6 +20,8 @@ export { GridPattern } from "./GridPattern";
|
|
|
20
20
|
export type { GridPatternProps, GridPatternFadeDirection, } from "./GridPattern";
|
|
21
21
|
export { DotPattern } from "./DotPattern";
|
|
22
22
|
export type { DotPatternProps, DotPatternFadeDirection, } from "./DotPattern";
|
|
23
|
+
export { StripePattern } from "./StripePattern";
|
|
24
|
+
export type { StripePatternProps, StripePatternFadeDirection, } from "./StripePattern";
|
|
23
25
|
export { NoiseOverlay } from "./NoiseOverlay";
|
|
24
26
|
export type { NoiseOverlayProps, NoiseBlendMode } from "./NoiseOverlay";
|
|
25
27
|
export { Spotlight } from "./Spotlight";
|
|
@@ -9,6 +9,7 @@ export { MagneticElement } from './MagneticElement/MagneticElement.js';
|
|
|
9
9
|
export { MorphingText } from './MorphingText/MorphingText.js';
|
|
10
10
|
export { GridPattern } from './GridPattern/GridPattern.js';
|
|
11
11
|
export { DotPattern } from './DotPattern/DotPattern.js';
|
|
12
|
+
export { StripePattern } from './StripePattern/StripePattern.js';
|
|
12
13
|
export { NoiseOverlay } from './NoiseOverlay/NoiseOverlay.js';
|
|
13
14
|
export { Spotlight } from './Spotlight/Spotlight.js';
|
|
14
15
|
export { SpotlightBorder } from './SpotlightBorder/SpotlightBorder.js';
|
|
@@ -10,7 +10,7 @@ const checkboxTheme = {
|
|
|
10
10
|
// Default state - light
|
|
11
11
|
"border-[var(--color-border)] bg-[var(--color-surface)] " +
|
|
12
12
|
// Default state - dark
|
|
13
|
-
"dark:bg-[var(--color-
|
|
13
|
+
"dark:bg-[var(--color-surface-raised)] " +
|
|
14
14
|
// Checked state - light
|
|
15
15
|
"checked:border-[var(--color-primary)] checked:bg-[var(--color-primary)] " +
|
|
16
16
|
// Checked state - dark
|
|
@@ -16,13 +16,13 @@ const cronInputTheme = {
|
|
|
16
16
|
// Field label
|
|
17
17
|
fieldLabelStyle: "text-xs font-medium text-[var(--color-text-secondary)] dark:text-[var(--color-neutral-400)]",
|
|
18
18
|
// Select dropdown
|
|
19
|
-
fieldSelectStyle: "w-full rounded-[var(--form-radius)] outline
|
|
19
|
+
fieldSelectStyle: "w-full rounded-[var(--form-radius)] outline-none shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
20
20
|
"bg-white text-[var(--color-text-primary)] cursor-pointer transition-all " +
|
|
21
|
-
"focus:
|
|
21
|
+
"focus:shadow-[inset_0_0_0_2px_var(--color-primary-600)] " +
|
|
22
22
|
"focus:ring-4 focus:ring-[var(--color-primary)]/10 " +
|
|
23
23
|
"disabled:cursor-not-allowed disabled:bg-[var(--color-background-disabled)] disabled:text-[var(--color-text-disabled)] " +
|
|
24
|
-
"dark:bg-[var(--color-neutral-900)] dark:
|
|
25
|
-
"dark:focus:
|
|
24
|
+
"dark:bg-[var(--color-neutral-900)] dark:shadow-[inset_0_0_0_1px_var(--color-neutral-700)] dark:text-[var(--color-neutral-200)] " +
|
|
25
|
+
"dark:focus:shadow-[inset_0_0_0_2px_var(--color-primary-500)] dark:focus:ring-[var(--color-primary-500)]/20",
|
|
26
26
|
// Size variants for field selects
|
|
27
27
|
fieldSelectSizes: {
|
|
28
28
|
sm: `${formsBaseTheme.sizes.sm.text} ${formsBaseTheme.sizes.sm.padding} ${formsBaseTheme.sizes.sm.height}`,
|
|
@@ -30,14 +30,14 @@ const cronInputTheme = {
|
|
|
30
30
|
lg: `${formsBaseTheme.sizes.lg.text} ${formsBaseTheme.sizes.lg.padding} ${formsBaseTheme.sizes.lg.height}`,
|
|
31
31
|
},
|
|
32
32
|
// Raw input field
|
|
33
|
-
rawInputStyle: "w-full rounded-[var(--form-radius)] outline
|
|
33
|
+
rawInputStyle: "w-full rounded-[var(--form-radius)] outline-none shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
34
34
|
"bg-white text-[var(--color-text-primary)] font-mono transition-all " +
|
|
35
35
|
"placeholder:text-[var(--color-text-placeholder)] focus:outline-none " +
|
|
36
|
-
"focus:
|
|
36
|
+
"focus:shadow-[inset_0_0_0_2px_var(--color-primary-600)] " +
|
|
37
37
|
"focus:ring-4 focus:ring-[var(--color-primary)]/10 " +
|
|
38
38
|
"disabled:cursor-not-allowed disabled:bg-[var(--color-background-disabled)] disabled:text-[var(--color-text-disabled)] " +
|
|
39
|
-
"dark:bg-[var(--color-neutral-900)] dark:
|
|
40
|
-
"dark:focus:
|
|
39
|
+
"dark:bg-[var(--color-neutral-900)] dark:shadow-[inset_0_0_0_1px_var(--color-neutral-700)] dark:text-[var(--color-neutral-200)] " +
|
|
40
|
+
"dark:focus:shadow-[inset_0_0_0_2px_var(--color-primary-500)] dark:focus:ring-[var(--color-primary-500)]/20",
|
|
41
41
|
// Size variants for raw input
|
|
42
42
|
rawInputSizes: {
|
|
43
43
|
sm: `${formsBaseTheme.sizes.sm.text} ${formsBaseTheme.sizes.sm.padding} ${formsBaseTheme.sizes.sm.height}`,
|
|
@@ -46,26 +46,26 @@ const cronInputTheme = {
|
|
|
46
46
|
},
|
|
47
47
|
// Mode toggle button
|
|
48
48
|
modeToggleStyle: "inline-flex items-center justify-center rounded-[var(--form-radius)] px-3 py-1.5 text-xs font-medium " +
|
|
49
|
-
"cursor-pointer transition-all outline
|
|
49
|
+
"cursor-pointer transition-all outline-none shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
50
50
|
"text-[var(--color-text-secondary)] bg-white hover:bg-[var(--color-background-secondary)] " +
|
|
51
|
-
"dark:bg-[var(--color-neutral-900)] dark:
|
|
51
|
+
"dark:bg-[var(--color-neutral-900)] dark:shadow-[inset_0_0_0_1px_var(--color-neutral-700)] dark:text-[var(--color-neutral-400)] " +
|
|
52
52
|
"dark:hover:bg-[var(--color-neutral-800)]",
|
|
53
53
|
// Active mode toggle
|
|
54
|
-
modeToggleActiveStyle: "
|
|
54
|
+
modeToggleActiveStyle: "shadow-[inset_0_0_0_1px_var(--color-primary)] bg-[var(--color-primary-50)] text-[var(--color-primary)] " +
|
|
55
55
|
"hover:bg-[var(--color-primary-50)] " +
|
|
56
|
-
"dark:
|
|
56
|
+
"dark:shadow-[inset_0_0_0_1px_var(--color-primary-500)] dark:bg-[var(--color-primary-500)]/10 dark:text-[var(--color-primary-400)] " +
|
|
57
57
|
"dark:hover:bg-[var(--color-primary-500)]/15",
|
|
58
58
|
// Preset button
|
|
59
59
|
presetButtonStyle: "inline-flex items-center rounded-[var(--form-radius)] px-2.5 py-1 text-xs font-medium " +
|
|
60
|
-
"cursor-pointer transition-all outline
|
|
60
|
+
"cursor-pointer transition-all outline-none shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
61
61
|
"text-[var(--color-text-secondary)] bg-white hover:bg-[var(--color-background-secondary)] " +
|
|
62
62
|
"hover:text-[var(--color-text-primary)] " +
|
|
63
|
-
"dark:bg-[var(--color-neutral-900)] dark:
|
|
63
|
+
"dark:bg-[var(--color-neutral-900)] dark:shadow-[inset_0_0_0_1px_var(--color-neutral-700)] dark:text-[var(--color-neutral-400)] " +
|
|
64
64
|
"dark:hover:bg-[var(--color-neutral-800)] dark:hover:text-[var(--color-neutral-200)]",
|
|
65
65
|
// Active preset button
|
|
66
|
-
presetButtonActiveStyle: "
|
|
66
|
+
presetButtonActiveStyle: "shadow-[inset_0_0_0_1px_var(--color-primary)] bg-[var(--color-primary-50)] text-[var(--color-primary)] " +
|
|
67
67
|
"hover:bg-[var(--color-primary-100)] " +
|
|
68
|
-
"dark:
|
|
68
|
+
"dark:shadow-[inset_0_0_0_1px_var(--color-primary-500)] dark:bg-[var(--color-primary-500)]/10 dark:text-[var(--color-primary-400)]",
|
|
69
69
|
// Human-readable description
|
|
70
70
|
descriptionStyle: "flex items-center gap-2 text-sm text-[var(--color-text-muted)] " +
|
|
71
71
|
"dark:text-[var(--color-neutral-400)]",
|
|
@@ -26,7 +26,7 @@ const DatePickerTrigger = ({ id, disabled, isOpen, state = "default", size = "md
|
|
|
26
26
|
// Apply focused styles when open - same pattern as SelectButton
|
|
27
27
|
const activeFocusState = isOpen
|
|
28
28
|
? focusStateStyle ||
|
|
29
|
-
"
|
|
29
|
+
"shadow-[inset_0_0_0_2px_var(--color-primary-600)] ring-4 ring-[var(--color-primary)]/10"
|
|
30
30
|
: "";
|
|
31
31
|
return (jsx("div", { className: "relative w-full", children: jsxs("button", { ref: triggerRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconEndPadding, triggerButtonStyle, "text-left", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Open calendar", "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, children: [children, jsx("span", { className: iconEndStyle, children: jsx(CalendarIcon, { className: "h-4 w-4" }) })] }) }));
|
|
32
32
|
};
|
|
@@ -35,7 +35,7 @@ const dateRangePickerTheme = {
|
|
|
35
35
|
mobilePresetsContainerStyle: "sm:hidden px-3 pt-3 pb-1",
|
|
36
36
|
mobilePresetButtonStyle: "whitespace-nowrap px-3 py-1.5 text-xs font-medium rounded-full transition-colors cursor-pointer shrink-0 " +
|
|
37
37
|
"bg-[var(--color-background-secondary)] text-[var(--color-text-secondary)] " +
|
|
38
|
-
"hover:bg-[var(--color-
|
|
38
|
+
"hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] " +
|
|
39
39
|
"data-[active=true]:bg-[var(--color-primary-50)] data-[active=true]:text-[var(--color-primary)] " +
|
|
40
40
|
"dark:bg-[var(--color-neutral-800)] dark:text-[var(--color-neutral-300)] " +
|
|
41
41
|
"dark:hover:bg-[var(--color-neutral-700)] " +
|
|
@@ -24,7 +24,7 @@ const DateRangePickerTrigger = ({ id, disabled, isOpen, state = "default", size
|
|
|
24
24
|
const triggerButtonStyle = theme.triggerButtonStyle || "";
|
|
25
25
|
const activeFocusState = isOpen
|
|
26
26
|
? focusStateStyle ||
|
|
27
|
-
"
|
|
27
|
+
"shadow-[inset_0_0_0_2px_var(--color-primary-600)] ring-4 ring-[var(--color-primary)]/10"
|
|
28
28
|
: "";
|
|
29
29
|
return (jsx("div", { className: "relative w-full", children: jsxs("button", { ref: triggerRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconEndPadding, triggerButtonStyle, "text-left", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Open date range picker", "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, children: [children, jsx("span", { className: iconEndStyle, children: jsx(CalendarIcon, { className: "h-4 w-4" }) })] }) }));
|
|
30
30
|
};
|
|
@@ -78,7 +78,6 @@ import '../../core/Sidebar/SidebarContent.js';
|
|
|
78
78
|
import '../../core/Sidebar/SidebarFooter.js';
|
|
79
79
|
import '../../core/Sidebar/SidebarNav.js';
|
|
80
80
|
import '../../core/Sidebar/SidebarNavGroup.js';
|
|
81
|
-
import 'motion/react';
|
|
82
81
|
import '../../core/Sidebar/SidebarToggle.js';
|
|
83
82
|
import '../../core/Sidebar/SidebarMobileTrigger.js';
|
|
84
83
|
import '../../core/Sidebar/SidebarUserProfile.js';
|
|
@@ -89,6 +88,7 @@ import '../../core/ContextMenu/ContextMenu.js';
|
|
|
89
88
|
import '../../core/ContextMenu/ContextMenu.theme.js';
|
|
90
89
|
import '../../core/Pill/Pill.js';
|
|
91
90
|
import '../../core/Icon/Icon.js';
|
|
91
|
+
import '../../core/ImagePlaceholder/ImagePlaceholder.js';
|
|
92
92
|
import { FormLabel } from '../FormLabel/FormLabel.js';
|
|
93
93
|
|
|
94
94
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import React__default, { useState, useEffect } from 'react';
|
|
3
3
|
import { useReducedMotion, motion, AnimatePresence } from 'motion/react';
|
|
4
|
-
import { DocumentIcon, TrashIcon,
|
|
4
|
+
import { DocumentIcon, TrashIcon, ArrowPathIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
5
5
|
import { Icon } from '../../core/Icon/Icon.js';
|
|
6
6
|
import { fileTypes } from '../../core/Icon/data/fileTypes.js';
|
|
7
7
|
import { cn } from '../../../utils/cn.js';
|
|
@@ -39,27 +39,38 @@ const RadialProgress = ({ progress, status, strokeWidth, trackColor, progressCol
|
|
|
39
39
|
setShowRemove(false);
|
|
40
40
|
}, [isComplete, completionDelay]);
|
|
41
41
|
const handleClick = () => {
|
|
42
|
-
if (
|
|
43
|
-
onRetry();
|
|
42
|
+
if (!onRemove)
|
|
44
43
|
return;
|
|
44
|
+
if (confirmRemove) {
|
|
45
|
+
setConfirmOpen(true);
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
setConfirmOpen(true);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
onRemove();
|
|
52
|
-
}
|
|
47
|
+
else {
|
|
48
|
+
onRemove();
|
|
53
49
|
}
|
|
54
50
|
};
|
|
55
51
|
const handleConfirm = () => {
|
|
56
52
|
setConfirmOpen(false);
|
|
57
53
|
onRemove === null || onRemove === void 0 ? void 0 : onRemove();
|
|
58
54
|
};
|
|
59
|
-
|
|
60
|
-
const
|
|
55
|
+
// Uploading/complete: the radial doubles as the cancel/remove target.
|
|
56
|
+
const isClickable = !!onRemove;
|
|
57
|
+
const ariaLabel = onRemove ? "Remove file" : undefined;
|
|
58
|
+
// Confirm popover shared by the radial (cancel/remove) and the error-state delete button.
|
|
59
|
+
const confirmPopover = (trigger, label, offsetValue) => (jsxs(Popover, { isOpen: confirmOpen, onOpenChange: (open) => {
|
|
60
|
+
setConfirmOpen(open);
|
|
61
|
+
if (!open)
|
|
62
|
+
setIsHovered(false);
|
|
63
|
+
}, placement: "bottom-end", offset: offsetValue, triggerType: "click", children: [jsx(Popover.Trigger, { children: trigger }), jsxs(Popover.Body, { className: "flex items-center p-1 border-[0.5px] rounded-xl gap-0.5 bg-opacity-50 backdrop-blur-md", children: [jsx("span", { className: "text-sm font-semibold text-[var(--color-text-secondary)] px-3", children: label }), jsxs("div", { className: "flex gap-1", children: [jsx(Button, { size: "sm", color: "danger", variant: "filled", onClick: handleConfirm, children: "Yes" }), jsx(Button, { size: "sm", variant: "soft", color: "neutral", onClick: () => setConfirmOpen(false), children: "No" })] })] })] }));
|
|
61
64
|
// Shared remove button used after completion tick
|
|
62
65
|
const removeButton = (jsx(motion.div, { initial: shouldReduceMotion ? false : REMOVE_BUTTON_ENTER.initial, animate: REMOVE_BUTTON_ENTER.animate, transition: REMOVE_BUTTON_ENTER.transition, className: "shrink-0", children: jsx(Button, { size: "sm", variant: "soft", color: "neutral", "aria-label": "Remove file", iconOnly: true, className: "rounded-full size-8", onClick: confirmRemove ? undefined : () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), children: jsx(TrashIcon, { className: "size-3.5", strokeWidth: 2 }) }) }, "remove-btn"));
|
|
66
|
+
// Error state: retry + delete buttons (no progress arc/percentage).
|
|
67
|
+
if (isError) {
|
|
68
|
+
const deleteButton = onRemove ? (jsx(Button, { size: "sm", variant: "soft", color: "neutral", "aria-label": "Remove file", iconOnly: true, className: "rounded-full size-8", onClick: confirmRemove ? undefined : () => onRemove(), children: jsx(TrashIcon, { className: "size-3.5", strokeWidth: 2 }) })) : null;
|
|
69
|
+
return (jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [onRetry && (jsx(Button, { size: "sm", variant: "soft", color: "neutral", "aria-label": "Retry upload", iconOnly: true, className: "rounded-full size-8", onClick: () => onRetry(), children: jsx(ArrowPathIcon, { className: "size-3.5", strokeWidth: 2 }) })), deleteButton &&
|
|
70
|
+
(confirmRemove
|
|
71
|
+
? confirmPopover(deleteButton, "Remove file?", 6)
|
|
72
|
+
: deleteButton)] }));
|
|
73
|
+
}
|
|
63
74
|
const radialElement = (jsxs("div", { className: cn("relative shrink-0", sizeClass, isClickable && "cursor-pointer"), onMouseEnter: () => setIsHovered(true), onMouseLeave: () => {
|
|
64
75
|
if (!confirmOpen)
|
|
65
76
|
setIsHovered(false);
|
|
@@ -70,18 +81,14 @@ const RadialProgress = ({ progress, status, strokeWidth, trackColor, progressCol
|
|
|
70
81
|
handleClick();
|
|
71
82
|
}
|
|
72
83
|
}
|
|
73
|
-
: undefined, children: [jsxs("svg", { viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`, className: "w-full h-full -rotate-90", children: [jsx("circle", { cx: CENTER, cy: CENTER, r: radius, fill: "none", stroke: trackColor, strokeWidth: strokeWidth }), jsx(motion.circle, { cx: CENTER, cy: CENTER, r: radius, fill: "none", stroke: progressColor, strokeWidth: strokeWidth, strokeLinecap: "round", strokeDasharray: circumference, initial: { strokeDashoffset: circumference }, animate: { strokeDashoffset: isComplete ? 0 : offset }, transition: shouldReduceMotion ? { duration: 0 } : PROGRESS_SPRING })] }), jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: jsxs(AnimatePresence, { mode: "wait", initial: false, children: [isUploading && !isHovered && (jsx(motion.span, { className: centerTextStyle, ...(shouldReduceMotion ? {} : CROSSFADE), children: progress }, "percent")), isUploading && isHovered && onRemove && (jsx(motion.span, { ...(shouldReduceMotion ? {} : CROSSFADE), children: jsx(XMarkIcon, { className: "size-4 text-[var(--color-text-muted)]", strokeWidth: 2.5 }) }, "close")), isComplete && !showRemove && (jsx(motion.span, { ...(shouldReduceMotion ? {} : CROSSFADE), children: jsx("svg", { viewBox: "0 0 24 24", fill: "none", className: cn("size-4", statusIconColor), children: jsx("path", { d: "M5 13l4.5 4.5L19 7", stroke: "currentColor", strokeWidth: 3.5, strokeLinecap: "round", strokeLinejoin: "round" }) }) }, "check"))
|
|
84
|
+
: undefined, children: [jsxs("svg", { viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`, className: "w-full h-full -rotate-90", children: [jsx("circle", { cx: CENTER, cy: CENTER, r: radius, fill: "none", stroke: trackColor, strokeWidth: strokeWidth }), jsx(motion.circle, { cx: CENTER, cy: CENTER, r: radius, fill: "none", stroke: progressColor, strokeWidth: strokeWidth, strokeLinecap: "round", strokeDasharray: circumference, initial: { strokeDashoffset: circumference }, animate: { strokeDashoffset: isComplete ? 0 : offset }, transition: shouldReduceMotion ? { duration: 0 } : PROGRESS_SPRING })] }), jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: jsxs(AnimatePresence, { mode: "wait", initial: false, children: [isUploading && !isHovered && (jsx(motion.span, { className: centerTextStyle, ...(shouldReduceMotion ? {} : CROSSFADE), children: progress }, "percent")), isUploading && isHovered && onRemove && (jsx(motion.span, { ...(shouldReduceMotion ? {} : CROSSFADE), children: jsx(XMarkIcon, { className: "size-4 text-[var(--color-text-muted)]", strokeWidth: 2.5 }) }, "close")), isComplete && !showRemove && (jsx(motion.span, { ...(shouldReduceMotion ? {} : CROSSFADE), children: jsx("svg", { viewBox: "0 0 24 24", fill: "none", className: cn("size-4", statusIconColor), children: jsx("path", { d: "M5 13l4.5 4.5L19 7", stroke: "currentColor", strokeWidth: 3.5, strokeLinecap: "round", strokeLinejoin: "round" }) }) }, "check"))] }) })] }));
|
|
74
85
|
// Determine what to render: radial indicator or remove button
|
|
75
86
|
const showRemoveButton = isComplete && showRemove && onRemove;
|
|
76
87
|
const triggerElement = showRemoveButton ? removeButton : radialElement;
|
|
77
|
-
// No confirm popover needed — return the element directly
|
|
88
|
+
// No confirm popover needed — return the element directly.
|
|
78
89
|
if (!confirmRemove || !onRemove)
|
|
79
90
|
return triggerElement;
|
|
80
|
-
return (
|
|
81
|
-
setConfirmOpen(open);
|
|
82
|
-
if (!open)
|
|
83
|
-
setIsHovered(false);
|
|
84
|
-
}, placement: "bottom-end", offset: isComplete ? 2 : 6, triggerType: "click", children: [jsx(Popover.Trigger, { children: triggerElement }), jsxs(Popover.Body, { portal: false, className: "flex items-center p-0.5 border-[0.5px] rounded-xl gap-0.5", children: [jsx("span", { className: "text-base font-medium text-[var(--color-text-secondary)] px-3", children: isComplete ? "Remove file?" : "Cancel upload?" }), jsx(Button, { size: "sm", color: "danger", variant: "filled", onClick: handleConfirm, children: "Yes" }), jsx(Button, { size: "sm", variant: "soft", color: "neutral", onClick: () => setConfirmOpen(false), children: "No" })] })] }));
|
|
91
|
+
return confirmPopover(triggerElement, isComplete ? "Remove file?" : "Cancel upload?", isComplete ? 2 : 6);
|
|
85
92
|
};
|
|
86
93
|
// --- Main Component ---
|
|
87
94
|
const FileUploadProgress = React__default.forwardRef(({ items, onRemove, onRetry, showFileSize = true, confirmRemove = true, completionDelay = 1500, className, theme: themeOverrides, ...rest }, ref) => {
|
|
@@ -103,7 +110,7 @@ const FileUploadProgress = React__default.forwardRef(({ items, onRemove, onRetry
|
|
|
103
110
|
const clampedProgress = Math.min(100, Math.max(0, item.progress));
|
|
104
111
|
const ext = getFileExtension(item.file.name);
|
|
105
112
|
const hasFileTypeIcon = ext !== null && isKnownFileType(ext);
|
|
106
|
-
return (jsxs("div", { className: (_a = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.itemStyle) !== null && _a !== void 0 ? _a : t.itemStyle, children: [hasFileTypeIcon ? (jsx("span", { className: "shrink-0", children: jsx(Icon, { icon: ext, size: "lg" }) })) : (jsx("span", { className: cn((_b = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.iconContainerStyle) !== null && _b !== void 0 ? _b : t.iconContainerStyle, t.iconSize), children: jsx(DocumentIcon, { className: (_c = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.iconStyle) !== null && _c !== void 0 ? _c : t.iconStyle }) })), jsxs("div", { className: (_d = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.fileInfoStyle) !== null && _d !== void 0 ? _d : t.fileInfoStyle, children: [jsx("p", { className: (_e = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.fileNameStyle) !== null && _e !== void 0 ? _e : t.fileNameStyle, children: item.file.name }),
|
|
113
|
+
return (jsxs("div", { className: (_a = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.itemStyle) !== null && _a !== void 0 ? _a : t.itemStyle, children: [hasFileTypeIcon ? (jsx("span", { className: "shrink-0", children: jsx(Icon, { icon: ext, size: "lg" }) })) : (jsx("span", { className: cn((_b = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.iconContainerStyle) !== null && _b !== void 0 ? _b : t.iconContainerStyle, t.iconSize), children: jsx(DocumentIcon, { className: (_c = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.iconStyle) !== null && _c !== void 0 ? _c : t.iconStyle }) })), jsxs("div", { className: (_d = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.fileInfoStyle) !== null && _d !== void 0 ? _d : t.fileInfoStyle, children: [jsx("p", { className: (_e = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.fileNameStyle) !== null && _e !== void 0 ? _e : t.fileNameStyle, children: item.file.name }), item.status === "error" && item.error ? (jsx("p", { className: (_f = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.errorTextStyle) !== null && _f !== void 0 ? _f : t.errorTextStyle, children: item.error })) : (showFileSize && (jsx("p", { className: (_g = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.fileSizeStyle) !== null && _g !== void 0 ? _g : t.fileSizeStyle, children: formatFileSize(item.file.size) })))] }), jsx(RadialProgress, { progress: clampedProgress, status: item.status, strokeWidth: t.radialStroke, trackColor: (_h = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.radialTrackColor) !== null && _h !== void 0 ? _h : t.radialTrackColor, progressColor: radialProgressColors[item.status], centerTextStyle: (_j = themeOverrides === null || themeOverrides === void 0 ? void 0 : themeOverrides.radialCenterTextStyle) !== null && _j !== void 0 ? _j : t.radialCenterTextStyle, statusIconColor: statusIconColors[item.status], sizeClass: t.radialSize, shouldReduceMotion: shouldReduceMotion, confirmRemove: confirmRemove, completionDelay: completionDelay, onRemove: onRemove ? () => onRemove(item.id) : undefined, onRetry: onRetry ? () => onRetry(item.id) : undefined })] }, item.id));
|
|
107
114
|
}) }));
|
|
108
115
|
});
|
|
109
116
|
FileUploadProgress.displayName = "FileUploadProgress";
|
|
@@ -6,13 +6,13 @@ const fileUploadProgressTheme = {
|
|
|
6
6
|
fileInfoStyle: "flex-1 min-w-0",
|
|
7
7
|
fileNameStyle: "text-sm font-medium text-[var(--color-text-primary)] truncate",
|
|
8
8
|
fileSizeStyle: "text-xs text-[var(--color-text-muted)]",
|
|
9
|
-
radialTrackColor: "var(--color-
|
|
9
|
+
radialTrackColor: "var(--color-surface-sunken)",
|
|
10
10
|
radialProgressColors: {
|
|
11
11
|
uploading: "var(--color-primary)",
|
|
12
12
|
complete: "var(--color-success)",
|
|
13
13
|
error: "var(--color-danger)",
|
|
14
14
|
},
|
|
15
|
-
radialCenterTextStyle: "text-
|
|
15
|
+
radialCenterTextStyle: "text-xs tracking-tight font-semibold tabular-nums text-[var(--color-text-secondary)]",
|
|
16
16
|
statusIconColors: {
|
|
17
17
|
uploading: "",
|
|
18
18
|
complete: "text-[var(--color-success)]",
|