@luxfi/ui 7.0.0 → 7.1.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.
- package/dist/alert.cjs +162 -285
- package/dist/alert.js +162 -285
- package/dist/badge.cjs +162 -321
- package/dist/badge.d.cts +3 -7
- package/dist/badge.d.ts +3 -7
- package/dist/badge.js +162 -320
- package/dist/button.cjs +373 -484
- package/dist/button.d.cts +10 -7
- package/dist/button.d.ts +10 -7
- package/dist/button.js +373 -484
- package/dist/close-button.cjs +37 -18
- package/dist/close-button.d.cts +1 -1
- package/dist/close-button.d.ts +1 -1
- package/dist/close-button.js +37 -18
- package/dist/collapsible.cjs +125 -272
- package/dist/collapsible.js +125 -272
- package/dist/dialog.cjs +60 -44
- package/dist/dialog.d.cts +7 -8
- package/dist/dialog.d.ts +7 -8
- package/dist/dialog.js +60 -43
- package/dist/drawer.cjs +37 -13
- package/dist/drawer.js +37 -13
- package/dist/heading.cjs +3 -8
- package/dist/heading.js +3 -8
- package/dist/icon-button.cjs +213 -337
- package/dist/icon-button.d.cts +4 -4
- package/dist/icon-button.d.ts +4 -4
- package/dist/icon-button.js +213 -338
- package/dist/image.cjs +125 -272
- package/dist/image.js +125 -272
- package/dist/index.cjs +604 -700
- package/dist/index.d.cts +0 -3
- package/dist/index.d.ts +0 -3
- package/dist/index.js +604 -698
- package/dist/link.cjs +125 -272
- package/dist/link.js +125 -272
- package/dist/popover.cjs +55 -40
- package/dist/popover.js +55 -39
- package/dist/select.cjs +125 -272
- package/dist/select.js +125 -272
- package/dist/skeleton.cjs +125 -272
- package/dist/skeleton.js +125 -272
- package/dist/table.cjs +127 -274
- package/dist/table.js +127 -274
- package/dist/tag.cjs +184 -306
- package/dist/tag.js +184 -305
- package/dist/tooltip.cjs +22 -21
- package/dist/tooltip.d.cts +2 -3
- package/dist/tooltip.d.ts +2 -3
- package/dist/tooltip.js +22 -20
- package/package.json +9 -7
- package/src/badge.tsx +20 -31
- package/src/button.tsx +304 -238
- package/src/close-button.tsx +48 -22
- package/src/dialog.tsx +37 -47
- package/src/heading.tsx +8 -19
- package/src/icon-button.tsx +129 -104
- package/src/popover.tsx +30 -44
- package/src/skeleton.tsx +96 -144
- package/src/tooltip.tsx +54 -47
package/src/popover.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Popover as GuiPopover } from '@hanzogui/popover';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
import { cn } from './utils';
|
|
5
5
|
|
|
6
6
|
import { CloseButton } from './close-button';
|
|
7
7
|
|
|
8
|
-
// --- Utility: map Chakra-style placement string to
|
|
8
|
+
// --- Utility: map Chakra-style placement string to @hanzogui placement ---
|
|
9
9
|
|
|
10
10
|
type Side = 'top' | 'right' | 'bottom' | 'left';
|
|
11
11
|
type Align = 'start' | 'center' | 'end';
|
|
@@ -61,7 +61,7 @@ export interface PopoverRootProps {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// Stash positioning info in context so PopoverContent can read it.
|
|
64
|
-
interface
|
|
64
|
+
interface PopoverPositioningCtx {
|
|
65
65
|
readonly side: Side;
|
|
66
66
|
readonly align: Align;
|
|
67
67
|
readonly sideOffset: number;
|
|
@@ -71,7 +71,7 @@ interface PopoverPositioning {
|
|
|
71
71
|
readonly closeOnInteractOutside: boolean;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const PositioningContext = React.createContext<
|
|
74
|
+
const PositioningContext = React.createContext<PopoverPositioningCtx>({
|
|
75
75
|
side: 'bottom',
|
|
76
76
|
align: 'start',
|
|
77
77
|
sideOffset: 4,
|
|
@@ -90,8 +90,7 @@ export const PopoverRoot = (props: PopoverRootProps): React.ReactElement => {
|
|
|
90
90
|
positioning,
|
|
91
91
|
autoFocus = false,
|
|
92
92
|
closeOnInteractOutside = true,
|
|
93
|
-
|
|
94
|
-
// lazyMount and unmountOnExit are handled via forceMount on Portal/Content
|
|
93
|
+
// lazyMount and unmountOnExit are handled via keepChildrenMounted
|
|
95
94
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
96
95
|
lazyMount: _lazyMount,
|
|
97
96
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -111,7 +110,7 @@ export const PopoverRoot = (props: PopoverRootProps): React.ReactElement => {
|
|
|
111
110
|
|
|
112
111
|
const { side, align } = parsePlacement(mergedPositioning.placement);
|
|
113
112
|
|
|
114
|
-
const positioningValue = React.useMemo<
|
|
113
|
+
const positioningValue = React.useMemo<PopoverPositioningCtx>(() => ({
|
|
115
114
|
side,
|
|
116
115
|
align,
|
|
117
116
|
sideOffset: mergedPositioning.offset?.mainAxis ?? 4,
|
|
@@ -125,21 +124,26 @@ export const PopoverRoot = (props: PopoverRootProps): React.ReactElement => {
|
|
|
125
124
|
mergedPositioning.overflowPadding, autoFocus, closeOnInteractOutside,
|
|
126
125
|
]);
|
|
127
126
|
|
|
128
|
-
// Bridge Chakra-style onOpenChange ({ open }) to
|
|
127
|
+
// Bridge Chakra-style onOpenChange ({ open }) to @hanzogui (open)
|
|
129
128
|
const handleOpenChange = React.useCallback((isOpen: boolean) => {
|
|
130
129
|
onOpenChange?.({ open: isOpen });
|
|
131
130
|
}, [ onOpenChange ]);
|
|
132
131
|
|
|
132
|
+
const placement = mergedPositioning.placement ?? 'bottom-start';
|
|
133
|
+
const offset = mergedPositioning.offset?.mainAxis ?? 4;
|
|
134
|
+
|
|
133
135
|
return (
|
|
134
136
|
<PositioningContext.Provider value={ positioningValue }>
|
|
135
|
-
<
|
|
137
|
+
<GuiPopover
|
|
136
138
|
open={ open }
|
|
137
139
|
defaultOpen={ defaultOpen }
|
|
138
140
|
onOpenChange={ handleOpenChange }
|
|
139
|
-
|
|
141
|
+
placement={ placement as any }
|
|
142
|
+
offset={ offset }
|
|
143
|
+
disableFocus={ !autoFocus }
|
|
140
144
|
>
|
|
141
145
|
{ children }
|
|
142
|
-
</
|
|
146
|
+
</GuiPopover>
|
|
143
147
|
</PositioningContext.Provider>
|
|
144
148
|
);
|
|
145
149
|
};
|
|
@@ -155,7 +159,7 @@ export const PopoverTrigger = React.forwardRef<
|
|
|
155
159
|
PopoverTriggerProps
|
|
156
160
|
>(function PopoverTrigger(props, ref) {
|
|
157
161
|
const { asChild = true, ...rest } = props;
|
|
158
|
-
return <
|
|
162
|
+
return <GuiPopover.Trigger asChild={ asChild ? 'except-style' : undefined } ref={ ref } { ...rest as any }/>;
|
|
159
163
|
});
|
|
160
164
|
|
|
161
165
|
// --- PopoverContent ---
|
|
@@ -174,7 +178,7 @@ export const PopoverContent = React.forwardRef<
|
|
|
174
178
|
HTMLDivElement,
|
|
175
179
|
PopoverContentProps
|
|
176
180
|
>(function PopoverContent(props, ref) {
|
|
177
|
-
const { portalled = true, portalRef, className, w, minW, maxW, paddingTop, style: styleProp, ...rest } = props;
|
|
181
|
+
const { portalled = true, portalRef: _portalRef, className, w, minW, maxW, paddingTop, style: styleProp, ...rest } = props;
|
|
178
182
|
const resolvedW = typeof w === 'object' ? (w as Record<string, string>).base ?? (w as Record<string, string>).lg : w;
|
|
179
183
|
const contentStyle: React.CSSProperties = {
|
|
180
184
|
...styleProp,
|
|
@@ -183,21 +187,11 @@ export const PopoverContent = React.forwardRef<
|
|
|
183
187
|
...(maxW ? { maxWidth: maxW } : {}),
|
|
184
188
|
...(paddingTop !== undefined ? { paddingTop: typeof paddingTop === 'number' ? `${ paddingTop * 4 }px` : paddingTop } : {}),
|
|
185
189
|
};
|
|
186
|
-
const positioning = React.useContext(PositioningContext);
|
|
187
|
-
|
|
188
|
-
const preventFocus = React.useCallback((e: Event) => e.preventDefault(), []);
|
|
189
|
-
const preventInteract = React.useCallback((e: Event) => e.preventDefault(), []);
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
<
|
|
191
|
+
return (
|
|
192
|
+
<GuiPopover.Content
|
|
193
193
|
ref={ ref }
|
|
194
|
-
|
|
195
|
-
align={ positioning.align }
|
|
196
|
-
sideOffset={ positioning.sideOffset }
|
|
197
|
-
alignOffset={ positioning.alignOffset }
|
|
198
|
-
collisionPadding={ positioning.collisionPadding }
|
|
199
|
-
onOpenAutoFocus={ positioning.autoFocus ? undefined : preventFocus }
|
|
200
|
-
onInteractOutside={ positioning.closeOnInteractOutside ? undefined : preventInteract }
|
|
194
|
+
unstyled
|
|
201
195
|
className={ cn(
|
|
202
196
|
'z-50 rounded-lg border border-[var(--color-popover-border,var(--color-border-divider))]',
|
|
203
197
|
'bg-[var(--color-popover-bg,var(--color-dialog-bg))]',
|
|
@@ -210,19 +204,9 @@ export const PopoverContent = React.forwardRef<
|
|
|
210
204
|
className,
|
|
211
205
|
) }
|
|
212
206
|
style={ Object.keys(contentStyle).length > 0 ? contentStyle : undefined }
|
|
213
|
-
{ ...rest }
|
|
207
|
+
{ ...rest as any }
|
|
214
208
|
/>
|
|
215
209
|
);
|
|
216
|
-
|
|
217
|
-
if (!portalled) {
|
|
218
|
-
return content;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return (
|
|
222
|
-
<RadixPopover.Portal container={ portalRef?.current ?? undefined }>
|
|
223
|
-
{ content }
|
|
224
|
-
</RadixPopover.Portal>
|
|
225
|
-
);
|
|
226
210
|
});
|
|
227
211
|
|
|
228
212
|
// --- PopoverArrow ---
|
|
@@ -235,10 +219,11 @@ export const PopoverArrow = React.forwardRef<
|
|
|
235
219
|
>(function PopoverArrow(props, ref) {
|
|
236
220
|
const { className, ...rest } = props;
|
|
237
221
|
return (
|
|
238
|
-
<
|
|
222
|
+
<GuiPopover.Arrow
|
|
239
223
|
ref={ ref }
|
|
224
|
+
unstyled
|
|
240
225
|
className={ cn('fill-[var(--color-popover-bg,var(--color-dialog-bg))]', className) }
|
|
241
|
-
{ ...rest }
|
|
226
|
+
{ ...rest as any }
|
|
242
227
|
/>
|
|
243
228
|
);
|
|
244
229
|
});
|
|
@@ -253,14 +238,15 @@ export const PopoverCloseTrigger = React.forwardRef<
|
|
|
253
238
|
>(function PopoverCloseTrigger(props, ref) {
|
|
254
239
|
const { className, ...rest } = props;
|
|
255
240
|
return (
|
|
256
|
-
<
|
|
241
|
+
<GuiPopover.Close
|
|
242
|
+
unstyled
|
|
257
243
|
className={ cn('absolute top-1 right-1', className) }
|
|
258
|
-
{ ...rest }
|
|
244
|
+
{ ...rest as any }
|
|
259
245
|
asChild
|
|
260
246
|
ref={ ref }
|
|
261
247
|
>
|
|
262
248
|
<CloseButton/>
|
|
263
|
-
</
|
|
249
|
+
</GuiPopover.Close>
|
|
264
250
|
);
|
|
265
251
|
});
|
|
266
252
|
|
|
@@ -281,9 +267,9 @@ export const PopoverCloseTriggerWrapper = React.forwardRef<
|
|
|
281
267
|
}
|
|
282
268
|
|
|
283
269
|
return (
|
|
284
|
-
<
|
|
270
|
+
<GuiPopover.Close ref={ ref } { ...rest as any } asChild>
|
|
285
271
|
{ children }
|
|
286
|
-
</
|
|
272
|
+
</GuiPopover.Close>
|
|
287
273
|
);
|
|
288
274
|
});
|
|
289
275
|
|
package/src/skeleton.tsx
CHANGED
|
@@ -12,7 +12,7 @@ export interface SkeletonProps extends Omit<React.HTMLAttributes<HTMLDivElement>
|
|
|
12
12
|
/** When true, the Skeleton wraps its single child element instead of adding a wrapper div. */
|
|
13
13
|
readonly asChild?: boolean;
|
|
14
14
|
|
|
15
|
-
// Legacy Chakra style-prop shims — converted to
|
|
15
|
+
// Legacy Chakra style-prop shims — converted to inline style.
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
17
|
readonly w?: any;
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -59,166 +59,124 @@ export interface SkeletonProps extends Omit<React.HTMLAttributes<HTMLDivElement>
|
|
|
59
59
|
readonly background?: string;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
// Style-prop shim names that must NOT leak into the DOM.
|
|
63
|
+
const SHIM_PROPS = [
|
|
64
|
+
'w', 'h', 'minW', 'maxW', 'display', 'flexGrow', 'flexShrink', 'flexBasis',
|
|
65
|
+
'fontWeight', 'textStyle', 'borderRadius', 'alignSelf', 'alignItems',
|
|
66
|
+
'justifyContent', 'color', 'mt', 'mb', 'ml', 'mr', 'height', 'overflow',
|
|
67
|
+
'whiteSpace', 'textOverflow', 'textTransform', 'gap', 'gridTemplateColumns',
|
|
68
|
+
'minWidth', 'boxSize', 'py', 'px', 'p', 'hideBelow', 'fontSize', 'flexWrap',
|
|
69
|
+
'wordBreak', 'lineHeight', 'marginRight', 'position', 'background',
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
72
|
+
const S = 4; // Chakra spacing scale
|
|
73
|
+
|
|
74
|
+
/** Resolve a dimension value (number → px via spacing scale, responsive object → first value). */
|
|
75
|
+
function dim(v: unknown): string | undefined {
|
|
76
|
+
if (v == null) return undefined;
|
|
77
|
+
if (typeof v === 'number') return `${ v * S }px`;
|
|
78
|
+
if (typeof v === 'string') return v;
|
|
79
|
+
if (typeof v === 'object') {
|
|
80
|
+
const o = v as Record<string, string>;
|
|
81
|
+
return o.base ?? o.lg ?? o.xl ?? Object.values(o)[0];
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const rest: Record<string, unknown> = {};
|
|
76
|
-
|
|
77
|
-
function resolveDimension(value: unknown): string | undefined {
|
|
78
|
-
if (value === undefined || value === null) return undefined;
|
|
79
|
-
if (typeof value === 'number') return `${ value * SPACING_SCALE }px`;
|
|
80
|
-
if (typeof value === 'string') return value;
|
|
81
|
-
if (typeof value === 'object') {
|
|
82
|
-
const obj = value as Record<string, string>;
|
|
83
|
-
return obj.base ?? obj.lg ?? obj.xl ?? Object.values(obj)[0];
|
|
84
|
-
}
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
86
|
+
/** Convert a spacing number|string via the 4px scale. */
|
|
87
|
+
function sp(v: number | string | undefined): string | undefined {
|
|
88
|
+
if (v === undefined) return undefined;
|
|
89
|
+
return typeof v === 'number' ? `${ v * S }px` : v;
|
|
90
|
+
}
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
case 'textOverflow': style.textOverflow = value as string; break;
|
|
121
|
-
case 'textTransform': style.textTransform = value as string; break;
|
|
122
|
-
case 'gap': style.gap = typeof value === 'number' ? `${ value * SPACING_SCALE }px` : value as string; break;
|
|
123
|
-
case 'gridTemplateColumns': style.gridTemplateColumns = value as string; break;
|
|
124
|
-
case 'minWidth': style.minWidth = value as string; break;
|
|
125
|
-
case 'boxSize': {
|
|
126
|
-
const s = typeof value === 'number' ? `${ value * SPACING_SCALE }px` : value as string; style.width = s; style.height = s; break;
|
|
127
|
-
}
|
|
128
|
-
case 'py': {
|
|
129
|
-
const v = toStylePx(value as number | string); style.paddingTop = v; style.paddingBottom = v; break;
|
|
130
|
-
}
|
|
131
|
-
case 'px': {
|
|
132
|
-
const v = toStylePx(value as number | string); style.paddingLeft = v; style.paddingRight = v; break;
|
|
133
|
-
}
|
|
134
|
-
case 'p': {
|
|
135
|
-
const v = toStylePx(value as number | string); style.padding = v; break;
|
|
136
|
-
}
|
|
137
|
-
case 'hideBelow': break; // handled via className
|
|
138
|
-
case 'textStyle': break; // drop textStyle, not directly applicable
|
|
139
|
-
case 'fontSize': style.fontSize = value as string; break;
|
|
140
|
-
case 'flexWrap': style.flexWrap = value as React.CSSProperties['flexWrap']; break;
|
|
141
|
-
case 'wordBreak': style.wordBreak = value as React.CSSProperties['wordBreak']; break;
|
|
142
|
-
case 'lineHeight': style.lineHeight = value as string; break;
|
|
143
|
-
case 'marginRight': style.marginRight = value as string; break;
|
|
144
|
-
case 'position': style.position = value as React.CSSProperties['position']; break;
|
|
145
|
-
case 'background': style.background = value as string; break;
|
|
146
|
-
default: rest[key] = value; break;
|
|
147
|
-
}
|
|
92
|
+
/** Build a CSSProperties object from legacy Chakra style-prop shims. */
|
|
93
|
+
function shimToStyle(props: SkeletonProps): React.CSSProperties {
|
|
94
|
+
const s: React.CSSProperties = {};
|
|
95
|
+
if (props.w !== undefined) s.width = dim(props.w);
|
|
96
|
+
if (props.h !== undefined) s.height = dim(props.h);
|
|
97
|
+
if (props.minW !== undefined) s.minWidth = dim(props.minW);
|
|
98
|
+
if (props.maxW !== undefined) s.maxWidth = dim(props.maxW);
|
|
99
|
+
if (props.display !== undefined) s.display = props.display;
|
|
100
|
+
if (props.flexGrow !== undefined) s.flexGrow = props.flexGrow;
|
|
101
|
+
if (props.flexShrink !== undefined) s.flexShrink = props.flexShrink;
|
|
102
|
+
if (props.flexBasis !== undefined) s.flexBasis = props.flexBasis;
|
|
103
|
+
if (props.fontWeight !== undefined) s.fontWeight = props.fontWeight;
|
|
104
|
+
if (props.borderRadius !== undefined) s.borderRadius = props.borderRadius;
|
|
105
|
+
if (props.alignSelf !== undefined) s.alignSelf = props.alignSelf;
|
|
106
|
+
if (props.alignItems !== undefined) s.alignItems = props.alignItems;
|
|
107
|
+
if (props.justifyContent !== undefined) s.justifyContent = props.justifyContent;
|
|
108
|
+
if (props.color !== undefined) s.color = props.color;
|
|
109
|
+
if (props.mt !== undefined) s.marginTop = sp(props.mt);
|
|
110
|
+
if (props.mb !== undefined) s.marginBottom = sp(props.mb);
|
|
111
|
+
if (props.ml !== undefined) s.marginLeft = sp(props.ml);
|
|
112
|
+
if (props.mr !== undefined) s.marginRight = sp(props.mr);
|
|
113
|
+
if (props.height !== undefined) s.height = props.height;
|
|
114
|
+
if (props.overflow !== undefined) s.overflow = props.overflow;
|
|
115
|
+
if (props.whiteSpace !== undefined) s.whiteSpace = props.whiteSpace;
|
|
116
|
+
if (props.textOverflow !== undefined) s.textOverflow = props.textOverflow;
|
|
117
|
+
if (props.textTransform !== undefined) s.textTransform = props.textTransform;
|
|
118
|
+
if (props.gap !== undefined) s.gap = sp(props.gap);
|
|
119
|
+
if (props.gridTemplateColumns !== undefined) s.gridTemplateColumns = props.gridTemplateColumns;
|
|
120
|
+
if (props.minWidth !== undefined) s.minWidth = props.minWidth;
|
|
121
|
+
if (props.boxSize !== undefined) {
|
|
122
|
+
const v = typeof props.boxSize === 'number' ? `${ props.boxSize * S }px` : props.boxSize;
|
|
123
|
+
s.width = v; s.height = v;
|
|
148
124
|
}
|
|
125
|
+
if (props.py !== undefined) { const v = sp(props.py); s.paddingTop = v; s.paddingBottom = v; }
|
|
126
|
+
if (props.px !== undefined) { const v = sp(props.px); s.paddingLeft = v; s.paddingRight = v; }
|
|
127
|
+
if (props.p !== undefined) s.padding = sp(props.p);
|
|
128
|
+
if (props.fontSize !== undefined) s.fontSize = props.fontSize;
|
|
129
|
+
if (props.flexWrap !== undefined) s.flexWrap = props.flexWrap;
|
|
130
|
+
if (props.wordBreak !== undefined) s.wordBreak = props.wordBreak;
|
|
131
|
+
if (props.lineHeight !== undefined) s.lineHeight = props.lineHeight;
|
|
132
|
+
if (props.marginRight !== undefined) s.marginRight = props.marginRight;
|
|
133
|
+
if (props.position !== undefined) s.position = props.position;
|
|
134
|
+
if (props.background !== undefined) s.background = props.background;
|
|
135
|
+
return s;
|
|
136
|
+
}
|
|
149
137
|
|
|
150
|
-
|
|
138
|
+
/** Strip shim keys from props so they don't leak into the DOM. */
|
|
139
|
+
function stripShims<T extends Record<string, unknown>>(props: T): Omit<T, (typeof SHIM_PROPS)[number]> {
|
|
140
|
+
const out = { ...props };
|
|
141
|
+
for (const k of SHIM_PROPS) delete out[k];
|
|
142
|
+
return out as Omit<T, (typeof SHIM_PROPS)[number]>;
|
|
151
143
|
}
|
|
152
144
|
|
|
145
|
+
const SKELETON_CLASSES = [
|
|
146
|
+
'animate-skeleton-shimmer rounded-sm',
|
|
147
|
+
'bg-[linear-gradient(90deg,var(--color-skeleton-start)_0%,var(--color-skeleton-end)_50%,var(--color-skeleton-start)_100%)]',
|
|
148
|
+
'bg-[length:200%_100%]',
|
|
149
|
+
].join(' ');
|
|
150
|
+
|
|
151
|
+
const HIDE_BELOW_MAP: Record<string, string> = { lg: 'lg:hidden', md: 'md:hidden', sm: 'sm:hidden' };
|
|
152
|
+
|
|
153
153
|
export const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
|
|
154
154
|
function Skeleton(props, ref) {
|
|
155
|
-
const {
|
|
156
|
-
loading = false,
|
|
157
|
-
asChild,
|
|
158
|
-
className,
|
|
159
|
-
children,
|
|
160
|
-
style: styleProp,
|
|
161
|
-
// Destructure style-prop shims so they don't leak into DOM
|
|
162
|
-
w: _w, h: _h, minW: _minW, maxW: _maxW, display: _display,
|
|
163
|
-
flexGrow: _flexGrow, flexShrink: _flexShrink, flexBasis: _flexBasis,
|
|
164
|
-
fontWeight: _fontWeight, textStyle: _textStyle, borderRadius: _borderRadius,
|
|
165
|
-
alignSelf: _alignSelf, alignItems: _alignItems, justifyContent: _justifyContent,
|
|
166
|
-
color: _color, mt: _mt, mb: _mb, ml: _ml, mr: _mr, height: _height,
|
|
167
|
-
overflow: _overflow, whiteSpace: _whiteSpace, textOverflow: _textOverflow,
|
|
168
|
-
textTransform: _textTransform, gap: _gap, gridTemplateColumns: _gridTemplateColumns,
|
|
169
|
-
minWidth: _minWidth, boxSize: _boxSize, py: _py, px: _px, p: _p,
|
|
170
|
-
hideBelow: _hideBelow, fontSize: _fontSize, flexWrap: _flexWrap, wordBreak: _wordBreak, lineHeight: _lineHeight,
|
|
171
|
-
marginRight: _marginRight, position: _position, background: _background,
|
|
172
|
-
as: Component = 'div',
|
|
173
|
-
...htmlRest
|
|
174
|
-
} = props;
|
|
155
|
+
const { loading = false, asChild, className, children, style: styleProp, as: Component = 'div', ...allRest } = props;
|
|
175
156
|
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
fontWeight: _fontWeight, textStyle: _textStyle, borderRadius: _borderRadius,
|
|
181
|
-
alignSelf: _alignSelf, alignItems: _alignItems, justifyContent: _justifyContent,
|
|
182
|
-
color: _color, mt: _mt, mb: _mb, ml: _ml, mr: _mr, height: _height,
|
|
183
|
-
overflow: _overflow, whiteSpace: _whiteSpace, textOverflow: _textOverflow,
|
|
184
|
-
textTransform: _textTransform, gap: _gap, gridTemplateColumns: _gridTemplateColumns,
|
|
185
|
-
minWidth: _minWidth, boxSize: _boxSize, py: _py, px: _px, p: _p,
|
|
186
|
-
hideBelow: _hideBelow, fontSize: _fontSize, flexWrap: _flexWrap, wordBreak: _wordBreak, lineHeight: _lineHeight,
|
|
187
|
-
marginRight: _marginRight, position: _position, background: _background,
|
|
188
|
-
});
|
|
189
|
-
const mergedStyle = Object.keys(shimStyle).length > 0 || styleProp ?
|
|
190
|
-
{ ...shimStyle, ...styleProp } :
|
|
191
|
-
undefined;
|
|
157
|
+
const shimStyle = shimToStyle(props);
|
|
158
|
+
const mergedStyle = Object.keys(shimStyle).length > 0 || styleProp ? { ...shimStyle, ...styleProp } : undefined;
|
|
159
|
+
const hideBelowClass = props.hideBelow ? HIDE_BELOW_MAP[props.hideBelow] : undefined;
|
|
160
|
+
const cls = hideBelowClass ? cn(className, hideBelowClass) : className;
|
|
192
161
|
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
const finalClassName = hideBelowClass ? cn(className, hideBelowClass) : className;
|
|
162
|
+
// Strip shim props from what reaches the DOM.
|
|
163
|
+
const htmlRest = stripShims(allRest);
|
|
196
164
|
|
|
197
165
|
if (!loading) {
|
|
198
|
-
|
|
199
|
-
if (asChild && React.isValidElement(children)) {
|
|
200
|
-
return children;
|
|
201
|
-
}
|
|
166
|
+
if (asChild && React.isValidElement(children)) return children;
|
|
202
167
|
return (
|
|
203
|
-
<Component ref={ ref } className={
|
|
168
|
+
<Component ref={ ref } className={ cls } style={ mergedStyle } { ...htmlRest }>
|
|
204
169
|
{ children }
|
|
205
170
|
</Component>
|
|
206
171
|
);
|
|
207
172
|
}
|
|
208
173
|
|
|
209
|
-
// When asChild is true and loading, wrap the child in skeleton styles
|
|
210
174
|
if (asChild && React.isValidElement(children)) {
|
|
211
175
|
return (
|
|
212
176
|
<Component
|
|
213
177
|
ref={ ref }
|
|
214
178
|
data-loading
|
|
215
|
-
className={ cn(
|
|
216
|
-
'animate-skeleton-shimmer rounded-sm',
|
|
217
|
-
'bg-[linear-gradient(90deg,var(--color-skeleton-start)_0%,var(--color-skeleton-end)_50%,var(--color-skeleton-start)_100%)]',
|
|
218
|
-
'bg-[length:200%_100%]',
|
|
219
|
-
'text-transparent [&_*]:invisible',
|
|
220
|
-
finalClassName,
|
|
221
|
-
) }
|
|
179
|
+
className={ cn(SKELETON_CLASSES, 'text-transparent [&_*]:invisible', cls) }
|
|
222
180
|
style={ mergedStyle }
|
|
223
181
|
{ ...htmlRest }
|
|
224
182
|
>
|
|
@@ -231,13 +189,7 @@ export const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
|
|
|
231
189
|
<Component
|
|
232
190
|
ref={ ref }
|
|
233
191
|
data-loading
|
|
234
|
-
className={ cn(
|
|
235
|
-
'animate-skeleton-shimmer rounded-sm',
|
|
236
|
-
'bg-[linear-gradient(90deg,var(--color-skeleton-start)_0%,var(--color-skeleton-end)_50%,var(--color-skeleton-start)_100%)]',
|
|
237
|
-
'bg-[length:200%_100%]',
|
|
238
|
-
children ? 'text-transparent [&_*]:invisible' : 'min-h-5',
|
|
239
|
-
finalClassName,
|
|
240
|
-
) }
|
|
192
|
+
className={ cn(SKELETON_CLASSES, children ? 'text-transparent [&_*]:invisible' : 'min-h-5', cls) }
|
|
241
193
|
style={ mergedStyle }
|
|
242
194
|
{ ...htmlRest }
|
|
243
195
|
>
|
package/src/tooltip.tsx
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
Tooltip as GuiTooltip,
|
|
3
|
+
TooltipGroup as GuiTooltipGroup,
|
|
4
|
+
} from '@hanzogui/tooltip';
|
|
2
5
|
import { useClickAway } from '@uidotdev/usehooks';
|
|
3
6
|
import * as React from 'react';
|
|
4
7
|
|
|
@@ -24,14 +27,21 @@ function useIsMobile(): boolean {
|
|
|
24
27
|
return isMobile;
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
/** Map placement string to @hanzogui Popper placement prop. */
|
|
31
|
+
function mapPlacement(p: string | undefined): string | undefined {
|
|
32
|
+
if (!p) return undefined;
|
|
33
|
+
// @hanzogui/popper uses same placement strings as floating-ui
|
|
34
|
+
return p;
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
export interface TooltipProps {
|
|
28
38
|
selected?: boolean;
|
|
29
39
|
showArrow?: boolean;
|
|
30
40
|
portalled?: boolean;
|
|
31
41
|
portalRef?: React.RefObject<HTMLElement>;
|
|
32
42
|
content: React.ReactNode;
|
|
33
|
-
contentProps?: React.
|
|
34
|
-
triggerProps?: React.
|
|
43
|
+
contentProps?: React.HTMLAttributes<HTMLDivElement>;
|
|
44
|
+
triggerProps?: React.HTMLAttributes<HTMLButtonElement>;
|
|
35
45
|
disabled?: boolean;
|
|
36
46
|
disableOnMobile?: boolean;
|
|
37
47
|
children?: React.ReactNode;
|
|
@@ -63,10 +73,8 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
63
73
|
children,
|
|
64
74
|
disabled,
|
|
65
75
|
disableOnMobile,
|
|
66
|
-
portalled = true,
|
|
67
76
|
content,
|
|
68
77
|
contentProps,
|
|
69
|
-
portalRef,
|
|
70
78
|
defaultOpen = false,
|
|
71
79
|
triggerProps,
|
|
72
80
|
closeDelay = 100,
|
|
@@ -127,58 +135,57 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
127
135
|
const defaultShowArrow = variant === 'popover' ? false : true;
|
|
128
136
|
const showArrow = showArrowProp !== undefined ? showArrowProp : defaultShowArrow;
|
|
129
137
|
|
|
130
|
-
const placement = positioning?.placement ?? 'top';
|
|
131
|
-
const side = placement.split('-')[0] as 'top' | 'bottom' | 'left' | 'right';
|
|
132
|
-
const align = placement.includes('-') ? (placement.split('-')[1] as 'start' | 'end') : undefined;
|
|
133
|
-
const sideOffset = positioning?.offset?.mainAxis ?? 4;
|
|
134
|
-
|
|
135
138
|
const isPopover = variant === 'popover';
|
|
136
139
|
|
|
140
|
+
const placement = mapPlacement(positioning?.placement) ?? 'top';
|
|
141
|
+
const offset = positioning?.offset?.mainAxis ?? 4;
|
|
142
|
+
|
|
137
143
|
return (
|
|
138
|
-
<
|
|
139
|
-
<
|
|
144
|
+
<GuiTooltipGroup delay={ openDelay }>
|
|
145
|
+
<GuiTooltip
|
|
140
146
|
open={ open }
|
|
141
147
|
onOpenChange={ handleOpenChange }
|
|
142
|
-
|
|
148
|
+
delay={{ open: openDelay, close: closeDelay }}
|
|
149
|
+
placement={ placement as any }
|
|
150
|
+
offset={ offset }
|
|
151
|
+
unstyled
|
|
143
152
|
>
|
|
144
|
-
<
|
|
153
|
+
<GuiTooltip.Trigger
|
|
145
154
|
ref={ open ? triggerRef : undefined }
|
|
146
155
|
asChild
|
|
147
|
-
|
|
148
|
-
{ ...triggerProps }
|
|
156
|
+
{...(isMobile ? { onPress: handleTriggerClick } : {})}
|
|
157
|
+
{ ...triggerProps as any }
|
|
149
158
|
>
|
|
150
159
|
{ children }
|
|
151
|
-
</
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
</RadixTooltip.Root>
|
|
181
|
-
</RadixTooltip.Provider>
|
|
160
|
+
</GuiTooltip.Trigger>
|
|
161
|
+
|
|
162
|
+
<GuiTooltip.Content
|
|
163
|
+
ref={ ref }
|
|
164
|
+
unstyled
|
|
165
|
+
className={ cn(
|
|
166
|
+
'z-[9999] overflow-hidden rounded-lg px-3 py-2 text-sm',
|
|
167
|
+
'animate-in fade-in-0 zoom-in-95',
|
|
168
|
+
isPopover && 'bg-[var(--color-popover-bg)] text-[var(--color-text-primary)]',
|
|
169
|
+
isPopover && 'shadow-[var(--shadow-popover)] border border-[var(--color-border-divider)] max-w-sm',
|
|
170
|
+
!isPopover && 'bg-[var(--color-tooltip-bg)] text-[var(--color-tooltip-fg)] max-w-xs',
|
|
171
|
+
contentProps?.className,
|
|
172
|
+
) }
|
|
173
|
+
onClick={ interactive ? handleContentClick : undefined }
|
|
174
|
+
{ ...(selected ? { 'data-selected': true } : {}) }
|
|
175
|
+
{ ...contentProps as any }
|
|
176
|
+
>
|
|
177
|
+
{ showArrow && (
|
|
178
|
+
<GuiTooltip.Arrow
|
|
179
|
+
unstyled
|
|
180
|
+
className={ cn(
|
|
181
|
+
isPopover ? 'fill-[var(--color-popover-bg)]' : 'fill-[var(--color-tooltip-bg)]',
|
|
182
|
+
) }
|
|
183
|
+
/>
|
|
184
|
+
) }
|
|
185
|
+
{ content }
|
|
186
|
+
</GuiTooltip.Content>
|
|
187
|
+
</GuiTooltip>
|
|
188
|
+
</GuiTooltipGroup>
|
|
182
189
|
);
|
|
183
190
|
},
|
|
184
191
|
);
|