@_linked/primitives 1.0.9 → 1.2.0

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 (80) hide show
  1. package/.github/workflows/publish.yml +74 -7
  2. package/CHANGELOG.md +65 -0
  3. package/README.md +62 -0
  4. package/lib/cjs/components/Badge.d.ts +30 -0
  5. package/lib/cjs/components/Badge.js +64 -0
  6. package/lib/cjs/components/Badge.js.map +1 -0
  7. package/lib/cjs/components/Badge.module.css +89 -0
  8. package/lib/cjs/components/Command.d.ts +6 -6
  9. package/lib/cjs/components/ConfirmDialog.d.ts +37 -0
  10. package/lib/cjs/components/ConfirmDialog.js +68 -0
  11. package/lib/cjs/components/ConfirmDialog.js.map +1 -0
  12. package/lib/cjs/components/ConfirmDialog.module.css +22 -0
  13. package/lib/cjs/components/ImageThumb.d.ts +17 -0
  14. package/lib/cjs/components/ImageThumb.js +90 -0
  15. package/lib/cjs/components/ImageThumb.js.map +1 -0
  16. package/lib/cjs/components/ImageThumb.module.css +134 -0
  17. package/lib/cjs/components/MultiSelect.d.ts +4 -4
  18. package/lib/cjs/components/SkeletonLoader.d.ts +14 -0
  19. package/lib/cjs/components/SkeletonLoader.js +71 -0
  20. package/lib/cjs/components/SkeletonLoader.js.map +1 -0
  21. package/lib/cjs/components/SkeletonLoader.module.css +83 -0
  22. package/lib/cjs/components/Slider.js +7 -3
  23. package/lib/cjs/components/Slider.js.map +1 -1
  24. package/lib/cjs/components/Spinner.d.ts +22 -3
  25. package/lib/cjs/components/Spinner.js +55 -3
  26. package/lib/cjs/components/Spinner.js.map +1 -1
  27. package/lib/cjs/components/Spinner.module.css +57 -10
  28. package/lib/cjs/components/TruncatedText.d.ts +16 -0
  29. package/lib/cjs/components/TruncatedText.js +130 -0
  30. package/lib/cjs/components/TruncatedText.js.map +1 -0
  31. package/lib/cjs/components/TruncatedText.module.css +162 -0
  32. package/lib/cjs/hooks/useReducedMotion.d.ts +12 -0
  33. package/lib/cjs/hooks/useReducedMotion.js +35 -0
  34. package/lib/cjs/hooks/useReducedMotion.js.map +1 -0
  35. package/lib/cjs/index.d.ts +6 -0
  36. package/lib/cjs/index.js +22 -0
  37. package/lib/cjs/index.js.map +1 -1
  38. package/lib/cjs/motion.d.ts +219 -0
  39. package/lib/cjs/motion.js +154 -0
  40. package/lib/cjs/motion.js.map +1 -0
  41. package/lib/cjs/package.json +6 -6
  42. package/lib/esm/components/Badge.d.ts +30 -0
  43. package/lib/esm/components/Badge.js +13 -0
  44. package/lib/esm/components/Badge.js.map +1 -0
  45. package/lib/esm/components/Badge.module.css +89 -0
  46. package/lib/esm/components/Command.d.ts +6 -6
  47. package/lib/esm/components/ConfirmDialog.d.ts +37 -0
  48. package/lib/esm/components/ConfirmDialog.js +29 -0
  49. package/lib/esm/components/ConfirmDialog.js.map +1 -0
  50. package/lib/esm/components/ConfirmDialog.module.css +22 -0
  51. package/lib/esm/components/ImageThumb.d.ts +17 -0
  52. package/lib/esm/components/ImageThumb.js +50 -0
  53. package/lib/esm/components/ImageThumb.js.map +1 -0
  54. package/lib/esm/components/ImageThumb.module.css +134 -0
  55. package/lib/esm/components/MultiSelect.d.ts +4 -4
  56. package/lib/esm/components/SkeletonLoader.d.ts +14 -0
  57. package/lib/esm/components/SkeletonLoader.js +31 -0
  58. package/lib/esm/components/SkeletonLoader.js.map +1 -0
  59. package/lib/esm/components/SkeletonLoader.module.css +83 -0
  60. package/lib/esm/components/Slider.js +6 -2
  61. package/lib/esm/components/Slider.js.map +1 -1
  62. package/lib/esm/components/Spinner.d.ts +22 -3
  63. package/lib/esm/components/Spinner.js +22 -3
  64. package/lib/esm/components/Spinner.js.map +1 -1
  65. package/lib/esm/components/Spinner.module.css +57 -10
  66. package/lib/esm/components/TruncatedText.d.ts +16 -0
  67. package/lib/esm/components/TruncatedText.js +83 -0
  68. package/lib/esm/components/TruncatedText.js.map +1 -0
  69. package/lib/esm/components/TruncatedText.module.css +162 -0
  70. package/lib/esm/hooks/useReducedMotion.d.ts +12 -0
  71. package/lib/esm/hooks/useReducedMotion.js +32 -0
  72. package/lib/esm/hooks/useReducedMotion.js.map +1 -0
  73. package/lib/esm/index.d.ts +6 -0
  74. package/lib/esm/index.js +8 -0
  75. package/lib/esm/index.js.map +1 -1
  76. package/lib/esm/motion.d.ts +219 -0
  77. package/lib/esm/motion.js +154 -0
  78. package/lib/esm/motion.js.map +1 -0
  79. package/lib/esm/package.json +6 -6
  80. package/package.json +6 -6
@@ -0,0 +1,134 @@
1
+ @import './helpers/mixins.css';
2
+ @import '@_linked/css/package.css';
3
+
4
+ /* ============================================================================
5
+ ImageThumb.module.css
6
+
7
+ Thumbnail image with enlarged hover preview.
8
+ Uses semantic tokens from theme.css.
9
+ ============================================================================ */
10
+
11
+ .root {
12
+ position: relative;
13
+ display: inline-flex;
14
+ flex-shrink: 0;
15
+ }
16
+
17
+ /* === Thumbnail === */
18
+ .thumb {
19
+ object-fit: cover;
20
+ border-radius: var(--radius-field);
21
+ border: 1px solid var(--surface-border);
22
+ cursor: pointer;
23
+ transition: box-shadow var(--transition-fast), transform var(--transition-fast);
24
+ background-color: var(--section-bg);
25
+ }
26
+
27
+ .thumb:hover {
28
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
29
+ transform: scale(1.05);
30
+ }
31
+
32
+ /* === Hover Preview Overlay === */
33
+ .previewOverlay {
34
+ position: fixed;
35
+ inset: 0;
36
+ z-index: 9999;
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ pointer-events: none;
41
+ }
42
+
43
+ .previewBackdrop {
44
+ position: absolute;
45
+ inset: 0;
46
+ background: rgba(0, 0, 0, 0.4);
47
+ opacity: 0;
48
+ transition: opacity 0.2s ease;
49
+ pointer-events: auto;
50
+ }
51
+
52
+ .previewBackdrop.visible {
53
+ opacity: 1;
54
+ }
55
+
56
+ .previewImage {
57
+ position: relative;
58
+ max-width: 80vw;
59
+ max-height: 80vh;
60
+ object-fit: contain;
61
+ border-radius: var(--radius-box);
62
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
63
+ z-index: 1;
64
+ opacity: 0;
65
+ transform: scale(0.92);
66
+ transition: opacity 0.2s ease, transform 0.2s ease;
67
+ pointer-events: auto;
68
+ }
69
+
70
+ .previewImage.visible {
71
+ opacity: 1;
72
+ transform: scale(1);
73
+ }
74
+
75
+ /* === Hover tooltip preview (positioned near thumbnail) === */
76
+ .hoverPreview {
77
+ position: fixed;
78
+ z-index: 10000;
79
+ pointer-events: none;
80
+ opacity: 0;
81
+ transform: scale(0.95);
82
+ transition: opacity 0.15s ease, transform 0.15s ease;
83
+ border-radius: var(--radius-box);
84
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
85
+ overflow: hidden;
86
+ background-color: var(--section-bg);
87
+ }
88
+
89
+ .hoverPreview.visible {
90
+ opacity: 1;
91
+ transform: scale(1);
92
+ }
93
+
94
+ .hoverPreviewImage {
95
+ display: block;
96
+ object-fit: cover;
97
+ border-radius: var(--radius-box);
98
+ }
99
+
100
+ /* === Dark mode adjustments === */
101
+ [data-theme='dark'] .thumb {
102
+ border-color: var(--surface-border);
103
+ }
104
+
105
+ [data-theme='dark'] .previewBackdrop {
106
+ background: rgba(0, 0, 0, 0.6);
107
+ }
108
+
109
+ [data-theme='dark'] .hoverPreview {
110
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
111
+ }
112
+
113
+ /* === Inline variant (used inside chips/badges) === */
114
+ .inlineThumb {
115
+ width: 20px;
116
+ height: 20px;
117
+ object-fit: cover;
118
+ border-radius: var(--radius-selector, 3px);
119
+ border: 1px solid var(--surface-border);
120
+ vertical-align: middle;
121
+ flex-shrink: 0;
122
+ }
123
+
124
+ /* === Reduced motion === */
125
+ @media (prefers-reduced-motion: reduce) {
126
+ .thumb:hover {
127
+ transform: none;
128
+ }
129
+ .hoverPreview,
130
+ .previewImage,
131
+ .previewBackdrop {
132
+ transition: none;
133
+ }
134
+ }
@@ -14,7 +14,7 @@ declare const Input: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<React.D
14
14
  ref?: React.Ref<HTMLInputElement>;
15
15
  } & {
16
16
  asChild?: boolean;
17
- }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "type" | "value" | "onChange"> & {
17
+ }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "type" | "onChange" | "value"> & {
18
18
  value?: string;
19
19
  onValueChange?: (search: string) => void;
20
20
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -36,7 +36,7 @@ declare const Item: React.ForwardRefExoticComponent<{
36
36
  ref?: React.Ref<HTMLDivElement>;
37
37
  } & {
38
38
  asChild?: boolean;
39
- }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "disabled" | "value" | "onSelect"> & {
39
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "onSelect" | "disabled" | "value"> & {
40
40
  disabled?: boolean;
41
41
  onSelect?: (value: string) => void;
42
42
  value?: string;
@@ -53,7 +53,7 @@ export declare const MultiSelect: {
53
53
  ref?: React.Ref<HTMLInputElement>;
54
54
  } & {
55
55
  asChild?: boolean;
56
- }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "type" | "value" | "onChange"> & {
56
+ }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "type" | "onChange" | "value"> & {
57
57
  value?: string;
58
58
  onValueChange?: (search: string) => void;
59
59
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -75,7 +75,7 @@ export declare const MultiSelect: {
75
75
  ref?: React.Ref<HTMLDivElement>;
76
76
  } & {
77
77
  asChild?: boolean;
78
- }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "disabled" | "value" | "onSelect"> & {
78
+ }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "onSelect" | "disabled" | "value"> & {
79
79
  disabled?: boolean;
80
80
  onSelect?: (value: string) => void;
81
81
  value?: string;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ interface SkeletonLoaderProps {
3
+ width?: string | number;
4
+ height?: string | number;
5
+ borderRadius?: string | number;
6
+ className?: string;
7
+ lines?: number;
8
+ animated?: boolean;
9
+ }
10
+ export declare function SkeletonLoader({ width, height, borderRadius, className, lines, animated, }: SkeletonLoaderProps): React.JSX.Element;
11
+ export declare function TableRowSkeleton({ columns }: {
12
+ columns?: number;
13
+ }): React.JSX.Element;
14
+ export default SkeletonLoader;
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import style from './SkeletonLoader.module.css';
3
+ export function SkeletonLoader({ width = '100%', height = 18, borderRadius = 4, className = '', lines = 1, animated = true, }) {
4
+ if (lines === 1) {
5
+ return (React.createElement("div", { className: `${style.skeleton} ${animated ? style.animated : ''} ${className}`, style: {
6
+ width,
7
+ height,
8
+ borderRadius,
9
+ } }));
10
+ }
11
+ return (React.createElement("div", { className: className }, Array.from({ length: lines }, (_, index) => (React.createElement("div", { key: index, className: `${style.skeleton} ${animated ? style.animated : ''} ${index < lines - 1 ? style.marginBottom : ''}`, style: {
12
+ width: index === lines - 1 ? '60%' : '100%',
13
+ height,
14
+ borderRadius,
15
+ } })))));
16
+ }
17
+ // Table row skeleton component
18
+ export function TableRowSkeleton({ columns = 5 }) {
19
+ return (React.createElement("tr", { className: style.tableRow },
20
+ React.createElement("td", { className: style.tableCell, style: { width: '48px' } },
21
+ React.createElement(SkeletonLoader, { width: 18, height: 18, borderRadius: 6 })),
22
+ Array.from({ length: columns }, (_, index) => (React.createElement("td", { key: index, className: style.tableCell },
23
+ React.createElement(SkeletonLoader, { width: "18vw", height: 18 })))),
24
+ React.createElement("td", { className: style.tableCell },
25
+ React.createElement("div", { className: style.actionButtons },
26
+ React.createElement(SkeletonLoader, { width: 22, height: 18, borderRadius: 6 }),
27
+ React.createElement(SkeletonLoader, { width: 22, height: 18, borderRadius: 6 }),
28
+ React.createElement(SkeletonLoader, { width: 22, height: 18, borderRadius: 6 })))));
29
+ }
30
+ export default SkeletonLoader;
31
+ //# sourceMappingURL=SkeletonLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkeletonLoader.js","sourceRoot":"","sources":["../../../src/components/SkeletonLoader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,MAAM,6BAA6B,CAAC;AAWhD,MAAM,UAAU,cAAc,CAAC,EAC7B,KAAK,GAAG,MAAM,EACd,MAAM,GAAG,EAAE,EACX,YAAY,GAAG,CAAC,EAChB,SAAS,GAAG,EAAE,EACd,KAAK,GAAG,CAAC,EACT,QAAQ,GAAG,IAAI,GACK;IACpB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,CACL,6BACE,SAAS,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,SAAS,EAAE,EAC7E,KAAK,EAAE;gBACL,KAAK;gBACL,MAAM;gBACN,YAAY;aACb,GACD,CACH,CAAC;IACJ,CAAC;IAED,OAAO,CACL,6BAAK,SAAS,EAAE,SAAS,IACtB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAC3C,6BACE,GAAG,EAAE,KAAK,EACV,SAAS,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,EAC/G,KAAK,EAAE;YACL,KAAK,EAAE,KAAK,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;YAC3C,MAAM;YACN,YAAY;SACb,GACD,CACH,CAAC,CACE,CACP,CAAC;AACJ,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,gBAAgB,CAAC,EAAE,OAAO,GAAG,CAAC,EAAwB;IACpE,OAAO,CACL,4BAAI,SAAS,EAAE,KAAK,CAAC,QAAQ;QAC3B,4BAAI,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;YACtD,oBAAC,cAAc,IAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,GAAI,CACvD;QACJ,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAC7C,4BAAI,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS;YACxC,oBAAC,cAAc,IAAC,KAAK,EAAC,MAAM,EAAC,MAAM,EAAE,EAAE,GAAI,CACxC,CACN,CAAC;QACF,4BAAI,SAAS,EAAE,KAAK,CAAC,SAAS;YAC5B,6BAAK,SAAS,EAAE,KAAK,CAAC,aAAa;gBACjC,oBAAC,cAAc,IAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,GAAI;gBAC1D,oBAAC,cAAc,IAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,GAAI;gBAC1D,oBAAC,cAAc,IAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,GAAI,CACtD,CACH,CACF,CACN,CAAC;AACJ,CAAC;AAED,eAAe,cAAc,CAAC"}
@@ -0,0 +1,83 @@
1
+ @import './helpers/mixins.css';
2
+ @import '@_linked/css/package.css';
3
+
4
+ /* ============================================================================
5
+ SkeletonLoader.module.css
6
+
7
+ Loading placeholder with shimmer animation.
8
+ Uses semantic tokens from theme.css.
9
+ ============================================================================ */
10
+
11
+ /* === Base Skeleton === */
12
+ .skeleton {
13
+ background: linear-gradient(
14
+ 90deg,
15
+ var(--skeleton-bg, var(--color-gray-100)) 25%,
16
+ var(--skeleton-highlight, var(--color-gray-200)) 50%,
17
+ var(--skeleton-bg, var(--color-gray-100)) 75%
18
+ );
19
+ background-size: 200% 100%;
20
+ animation: shimmer 1.5s infinite;
21
+ display: inline-block;
22
+ border-radius: var(--radius-selector, 4px);
23
+ }
24
+
25
+ /* === Animation Variant === */
26
+ .animated {
27
+ animation: shimmer 1.5s infinite;
28
+ }
29
+
30
+ /* === Spacing === */
31
+ .marginBottom {
32
+ margin-bottom: 0.5rem;
33
+ }
34
+
35
+ /* === Table Row Skeleton === */
36
+ .tableRow {
37
+ border-bottom: 1px solid var(--skeleton-row-border, var(--table-row-border));
38
+ }
39
+
40
+ /* === Table Cell Skeleton === */
41
+ .tableCell {
42
+ padding: var(--skeleton-row-padding-y, var(--surface-gap)) var(--skeleton-row-padding-x, var(--space-sm));
43
+ vertical-align: middle;
44
+ }
45
+
46
+ /* === Action Buttons Container === */
47
+ .actionButtons {
48
+ display: flex;
49
+ gap: var(--space-sm);
50
+ align-items: center;
51
+ justify-content: flex-end;
52
+ }
53
+
54
+ /* ============================================================================
55
+ Dark Mode
56
+ ============================================================================ */
57
+
58
+ [data-theme='dark'] .skeleton {
59
+ background: linear-gradient(
60
+ 90deg,
61
+ var(--skeleton-bg, var(--color-gray-800)) 25%,
62
+ var(--skeleton-highlight, var(--color-gray-700)) 50%,
63
+ var(--skeleton-bg, var(--color-gray-800)) 75%
64
+ );
65
+ background-size: 200% 100%;
66
+ }
67
+
68
+ [data-theme='dark'] .tableRow {
69
+ border-bottom-color: var(--skeleton-row-border, var(--table-row-border));
70
+ }
71
+
72
+ /* ============================================================================
73
+ Shimmer Animation
74
+ ============================================================================ */
75
+
76
+ @keyframes shimmer {
77
+ 0% {
78
+ background-position: -200% 0;
79
+ }
80
+ 100% {
81
+ background-position: 200% 0;
82
+ }
83
+ }
@@ -14,10 +14,14 @@ function extend(Component, baseClassName) {
14
14
  const Track = extend(SliderPrimitive.Track, style.Track);
15
15
  const Range = extend(SliderPrimitive.Range, style.Range);
16
16
  const Thumb = extend(SliderPrimitive.Thumb, style.Thumb);
17
- const Root = React.forwardRef(({ className, ...props }, ref) => (React.createElement(SliderPrimitive.Root, { ref: ref, className: cl(style.Root, className), ...props },
17
+ const Root = React.forwardRef(({ className, 'aria-label': ariaLabel, 'aria-disabled': ariaDisabled, ...props }, ref) => (
18
+ // Self-contained single-thumb slider. Forward the accessible name / disabled
19
+ // state to the actual `role="slider"` element (the Thumb) rather than the Root,
20
+ // so `getByRole('slider', { name })` and assistive tech can address the control.
21
+ React.createElement(SliderPrimitive.Root, { ref: ref, className: cl(style.Root, className), ...props },
18
22
  React.createElement(Track, null,
19
23
  React.createElement(Range, null)),
20
- React.createElement(Thumb, null))));
24
+ React.createElement(Thumb, { "aria-label": ariaLabel, "aria-disabled": ariaDisabled }))));
21
25
  Root.displayName = SliderPrimitive.Root.displayName;
22
26
  export const Slider = {
23
27
  Root,
@@ -1 +1 @@
1
- {"version":3,"file":"Slider.js","sourceRoot":"","sources":["../../../src/components/Slider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AAErD,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAE1D,SAAS,MAAM,CACb,SAAY,EACZ,aAAqB;IAErB,IAAI,QAAQ,GAAG,KAAK,CAAC,UAAU,CAG7B,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CACjC,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE;QAC7B,GAAG;QACH,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC;QACvC,GAAG,KAAK;KACT,CAAC,CACH,CAAC;IACF,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IAC7C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;AACzD,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;AACzD,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;AAEzD,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAG3B,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAClC,oBAAC,eAAe,CAAC,IAAI,IACnB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,KAChC,KAAK;IAET,oBAAC,KAAK;QACJ,oBAAC,KAAK,OAAG,CACH;IACR,oBAAC,KAAK,OAAG,CACY,CACxB,CAAC,CAAC;AACH,IAAI,CAAC,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;AAEpD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI;IACJ,KAAK;IACL,KAAK;IACL,KAAK;CACN,CAAC;AAEF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"Slider.js","sourceRoot":"","sources":["../../../src/components/Slider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AAErD,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAE1D,SAAS,MAAM,CACb,SAAY,EACZ,aAAqB;IAErB,IAAI,QAAQ,GAAG,KAAK,CAAC,UAAU,CAG7B,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CACjC,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE;QAC7B,GAAG;QACH,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC;QACvC,GAAG,KAAK;KACT,CAAC,CACH,CAAC;IACF,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IAC7C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;AACzD,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;AACzD,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;AAEzD,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAG3B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;AAC1F,6EAA6E;AAC7E,gFAAgF;AAChF,iFAAiF;AACjF,oBAAC,eAAe,CAAC,IAAI,IACnB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,KAChC,KAAK;IAET,oBAAC,KAAK;QACJ,oBAAC,KAAK,OAAG,CACH;IACR,oBAAC,KAAK,kBAAa,SAAS,mBAAiB,YAAY,GAAI,CACxC,CACxB,CAAC,CAAC;AACH,IAAI,CAAC,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;AAEpD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI;IACJ,KAAK;IACL,KAAK;IACL,KAAK;CACN,CAAC;AAEF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC"}
@@ -1,4 +1,23 @@
1
- import React from 'react';
2
- export declare function Spinner({ active }: {
1
+ import * as React from 'react';
2
+ export interface SpinnerProps {
3
+ /** Render nothing when false. */
3
4
  active?: boolean;
4
- }): React.JSX.Element;
5
+ size?: 'small' | 'medium' | 'large' | 'xlarge';
6
+ className?: string;
7
+ /** Announced to assistive technology. */
8
+ label?: string;
9
+ }
10
+ /**
11
+ * A loading indicator: a ring that inherits its colour, so it reads on any background.
12
+ *
13
+ * Two things it does that a bare CSS spin does not. It honours
14
+ * `prefers-reduced-motion` — a spinner is the most common thing to make someone with
15
+ * vestibular sensitivity look away — showing a still ring rather than nothing, so the
16
+ * "busy" signal survives. And it renders **nothing** when inactive, so it can be dropped
17
+ * into a scroll sentinel or a button without reserving space it is not using.
18
+ *
19
+ * The ring is an inner element rather than a border on the container. A border on the
20
+ * container is distorted the moment a flex parent stretches it; an explicitly sized inner
21
+ * element stays circular.
22
+ */
23
+ export declare function Spinner({ active, size, className, label, }: SpinnerProps): React.JSX.Element;
@@ -1,6 +1,25 @@
1
- import React from 'react';
1
+ import * as React from 'react';
2
2
  import style from './Spinner.module.css';
3
- export function Spinner({ active = true }) {
4
- return (React.createElement("div", { className: [style.spinner, active && style.active].join(' '), role: "progressbar", "aria-busy": active ? 'true' : 'false' }));
3
+ import { cl } from '@_linked/react/utils/ClassNames';
4
+ import { useReducedMotion } from '../hooks/useReducedMotion.js';
5
+ /**
6
+ * A loading indicator: a ring that inherits its colour, so it reads on any background.
7
+ *
8
+ * Two things it does that a bare CSS spin does not. It honours
9
+ * `prefers-reduced-motion` — a spinner is the most common thing to make someone with
10
+ * vestibular sensitivity look away — showing a still ring rather than nothing, so the
11
+ * "busy" signal survives. And it renders **nothing** when inactive, so it can be dropped
12
+ * into a scroll sentinel or a button without reserving space it is not using.
13
+ *
14
+ * The ring is an inner element rather than a border on the container. A border on the
15
+ * container is distorted the moment a flex parent stretches it; an explicitly sized inner
16
+ * element stays circular.
17
+ */
18
+ export function Spinner({ active = true, size = 'small', className, label = 'Loading', }) {
19
+ const reducedMotion = useReducedMotion();
20
+ if (!active)
21
+ return null;
22
+ return (React.createElement("div", { className: cl(style.spinner, style[size], className), role: "progressbar", "aria-busy": "true", "aria-label": label },
23
+ React.createElement("span", { className: cl(style.ring, reducedMotion && style.reducedMotion), "aria-hidden": "true" })));
5
24
  }
6
25
  //# sourceMappingURL=Spinner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../../src/components/Spinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,sBAAsB,CAAC;AAEzC,MAAM,UAAU,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE;IACvC,OAAO,CACL,6BACE,SAAS,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAC5D,IAAI,EAAC,aAAa,eACP,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,GACpC,CACH,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../../src/components/Spinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,MAAM,sBAAsB,CAAC;AACzC,OAAO,EAAC,EAAE,EAAC,MAAM,iCAAiC,CAAC;AACnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,8BAA8B,CAAC;AAW9D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,OAAO,CAAC,EACtB,MAAM,GAAG,IAAI,EACb,IAAI,GAAG,OAAO,EACd,SAAS,EACT,KAAK,GAAG,SAAS,GACJ;IACb,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IAEzC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,OAAO,CACL,6BACE,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,EACpD,IAAI,EAAC,aAAa,eACR,MAAM,gBACJ,KAAK;QAEjB,8BACE,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,IAAI,KAAK,CAAC,aAAa,CAAC,iBACnD,MAAM,GAClB,CACE,CACP,CAAC;AACJ,CAAC"}
@@ -2,20 +2,67 @@
2
2
  @import '@_linked/css/package.css';
3
3
 
4
4
  .spinner {
5
- border: var(--spinner-border-width, 4px) solid
6
- var(--spinner-track-color, transparent);
7
- border-top-color: var(--spinner-indicator-color, var(--meter-indicator-bg));
8
- border-radius: 50%;
5
+ display: inline-flex;
6
+ /* Never grow or shrink in a flex parent — that would distort the ring. */
7
+ flex: 0 0 auto;
8
+ align-items: center;
9
+ justify-content: center;
10
+ color: var(--spinner-indicator-color, var(--meter-indicator-bg, currentColor));
11
+ }
12
+
13
+ /* Sized explicitly rather than filling the container, so the ring stays a circle even when
14
+ a flex parent stretches the box around it. */
15
+ .ring {
16
+ box-sizing: border-box;
17
+ flex: 0 0 auto;
9
18
  width: var(--spinner-size, --spacing(4));
10
19
  height: var(--spinner-size, --spacing(4));
11
- animation: spin var(--spinner-animation-duration, 1s) linear infinite;
20
+ border-radius: 50%;
21
+ border: var(--spinner-border-width, 2px) solid currentColor;
22
+ border-top-color: var(--spinner-track-color, transparent);
23
+ opacity: 0.85;
24
+ animation: spinnerRotate var(--spinner-animation-duration, 0.7s) linear infinite;
12
25
  }
13
26
 
14
- @keyframes spin {
15
- 0% {
16
- transform: rotate(0deg);
17
- }
18
- 100% {
27
+ @keyframes spinnerRotate {
28
+ to {
19
29
  transform: rotate(360deg);
20
30
  }
21
31
  }
32
+
33
+ /* Size variants. Each sets `--spinner-size`, so a consumer that sets the variable directly
34
+ still wins — the variants are a shorthand for it, not a replacement. */
35
+ .small {
36
+ --spinner-size: --spacing(4);
37
+ }
38
+ .medium {
39
+ --spinner-size: --spacing(6);
40
+ }
41
+ .large {
42
+ --spinner-size: --spacing(8);
43
+ --spinner-border-width: 3px;
44
+ }
45
+ .xlarge {
46
+ --spinner-size: --spacing(12);
47
+ --spinner-border-width: 3px;
48
+ }
49
+
50
+ .small,
51
+ .medium,
52
+ .large,
53
+ .xlarge {
54
+ width: var(--spinner-size);
55
+ height: var(--spinner-size);
56
+ }
57
+
58
+ /* Reduced motion: freeze the ring, keep it visible. Removing it entirely would remove the
59
+ only signal that something is loading. */
60
+ .reducedMotion {
61
+ animation: none;
62
+ }
63
+
64
+ @media (prefers-reduced-motion: reduce) {
65
+ .ring {
66
+ animation: none;
67
+ }
68
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ interface TruncatedTextProps {
3
+ /** The full text content */
4
+ text: string;
5
+ /** Maximum number of characters to show before truncating (default: 80) */
6
+ maxLength?: number;
7
+ }
8
+ /**
9
+ * TruncatedText - Displays truncated text with hover/tap to show full content
10
+ *
11
+ * For long text fields, shows first N characters with ellipsis.
12
+ * On desktop: hover displays full text in a popover with copy button.
13
+ * On mobile: tap opens a dialog with full text and copy button.
14
+ */
15
+ declare function TruncatedText({ text, maxLength }: TruncatedTextProps): React.JSX.Element;
16
+ export { TruncatedText };
@@ -0,0 +1,83 @@
1
+ import { Dialog } from './Dialog.js';
2
+ import { HoverCard } from './HoverCard.js';
3
+ import { IconButton } from './IconButton.js';
4
+ import React, { useState, useEffect } from 'react';
5
+ import style from './TruncatedText.module.css';
6
+ /**
7
+ * Two inline glyphs rather than a dependency on an icon set.
8
+ *
9
+ * This component needs exactly two, and a library whose contract is "a behaviour source plus
10
+ * a stylesheet" should not make every consumer install an icon package to draw a tick. They
11
+ * inherit `currentColor`, so they follow the button they sit in.
12
+ */
13
+ const CheckGlyph = () => (React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" },
14
+ React.createElement("polyline", { points: "20 6 9 17 4 12" })));
15
+ const CopyGlyph = () => (React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" },
16
+ React.createElement("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
17
+ React.createElement("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })));
18
+ /**
19
+ * TruncatedText - Displays truncated text with hover/tap to show full content
20
+ *
21
+ * For long text fields, shows first N characters with ellipsis.
22
+ * On desktop: hover displays full text in a popover with copy button.
23
+ * On mobile: tap opens a dialog with full text and copy button.
24
+ */
25
+ function TruncatedText({ text, maxLength = 80 }) {
26
+ const [copied, setCopied] = useState(false);
27
+ const [isMobile, setIsMobile] = useState(false);
28
+ useEffect(() => {
29
+ // Check for touch device or small screen
30
+ const checkMobile = () => {
31
+ const hasTouchScreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
32
+ const isSmallScreen = window.matchMedia('(max-width: 768px)').matches;
33
+ setIsMobile(hasTouchScreen || isSmallScreen);
34
+ };
35
+ checkMobile();
36
+ window.addEventListener('resize', checkMobile);
37
+ return () => window.removeEventListener('resize', checkMobile);
38
+ }, []);
39
+ // If text is short enough, just render it directly
40
+ if (!text || text.length <= maxLength) {
41
+ return React.createElement("span", null, text || '-');
42
+ }
43
+ const truncatedText = text.slice(0, maxLength) + '...';
44
+ const handleCopy = async (e) => {
45
+ e.stopPropagation();
46
+ try {
47
+ await navigator.clipboard.writeText(text);
48
+ setCopied(true);
49
+ setTimeout(() => setCopied(false), 2000);
50
+ }
51
+ catch (err) {
52
+ console.error('Failed to copy text:', err);
53
+ }
54
+ };
55
+ // Mobile: Use Dialog triggered by tap
56
+ if (isMobile) {
57
+ return (React.createElement(Dialog.Root, null,
58
+ React.createElement(Dialog.Trigger, { asChild: true },
59
+ React.createElement("span", { className: style.truncatedText }, truncatedText)),
60
+ React.createElement(Dialog.Content, { className: style.dialogContent },
61
+ React.createElement(Dialog.Header, null,
62
+ React.createElement(Dialog.Title, null, "Full Text")),
63
+ React.createElement("div", { className: style.dialogBody },
64
+ React.createElement("div", { className: style.scrollArea },
65
+ React.createElement("p", { className: style.fullText }, text))),
66
+ React.createElement("div", { className: style.dialogFooter },
67
+ React.createElement(IconButton, { size: "small", variant: "ghost", className: style.copyButton, onClick: handleCopy, "aria-label": copied ? 'Copied!' : 'Copy to clipboard' }, copied ? (React.createElement(CheckGlyph, null)) : (React.createElement(CopyGlyph, null)))))));
68
+ }
69
+ // Desktop: Use HoverCard triggered by hover
70
+ return (React.createElement(HoverCard.Root, { openDelay: 300, closeDelay: 200 },
71
+ React.createElement(HoverCard.Trigger, { asChild: true },
72
+ React.createElement("span", { className: style.truncatedText }, truncatedText)),
73
+ React.createElement(HoverCard.Portal, null,
74
+ React.createElement(HoverCard.Content, { side: "top", align: "start", sideOffset: 8, className: style.hoverContent },
75
+ React.createElement("div", { className: style.contentWrapper },
76
+ React.createElement("div", { className: style.scrollArea },
77
+ React.createElement("p", { className: style.fullText }, text)),
78
+ React.createElement("div", { className: style.copyButtonWrapper },
79
+ React.createElement(IconButton, { size: "small", variant: "ghost", className: style.copyButton, onClick: handleCopy, "aria-label": copied ? 'Copied!' : 'Copy to clipboard' }, copied ? (React.createElement(CheckGlyph, null)) : (React.createElement(CopyGlyph, null))))),
80
+ React.createElement(HoverCard.Arrow, { className: style.arrow })))));
81
+ }
82
+ export { TruncatedText };
83
+ //# sourceMappingURL=TruncatedText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TruncatedText.js","sourceRoot":"","sources":["../../../src/components/TruncatedText.tsx"],"names":[],"mappings":"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"}