@astryxdesign/core 0.4.2 → 0.4.3
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/CHANGELOG.md +45 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +3 -19
- package/dist/Banner/Banner.d.ts +6 -2
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +71 -30
- package/dist/Banner/index.d.ts +14 -12
- package/dist/Banner/index.d.ts.map +1 -1
- package/dist/Banner/index.js +10 -8
- package/dist/Chat/ChatTokenizedText.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts +38 -4
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +96 -35
- package/dist/ComplexSelector/index.d.ts +2 -2
- package/dist/ComplexSelector/index.d.ts.map +1 -1
- package/dist/ComplexSelector/index.js +1 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +55 -12
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +4 -1
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +2 -4
- package/dist/Table/columnUtils.d.ts.map +1 -1
- package/dist/Table/columnUtils.js +4 -1
- package/dist/TextArea/TextArea.d.ts +6 -3
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +17 -6
- package/dist/TreeList/TreeList.js +2 -1
- package/dist/astryx.css +6 -4
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/theme/defineTheme.d.ts +8 -3
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +36 -47
- package/dist/theme/mergeComponents.d.ts +20 -0
- package/dist/theme/mergeComponents.d.ts.map +1 -0
- package/dist/theme/mergeComponents.js +56 -0
- package/dist/theme/onMediaTokens.d.ts +6 -1
- package/dist/theme/onMediaTokens.d.ts.map +1 -1
- package/dist/theme/onMediaTokens.js +11 -3
- package/dist/utils/characters.d.ts +27 -0
- package/dist/utils/characters.d.ts.map +1 -0
- package/dist/utils/characters.js +83 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/package.json +2 -2
- package/src/Avatar/Avatar.test.tsx +6 -1
- package/src/Avatar/Avatar.tsx +3 -21
- package/src/Banner/Banner.doc.mjs +9 -7
- package/src/Banner/Banner.test.tsx +68 -0
- package/src/Banner/Banner.tsx +97 -36
- package/src/Banner/index.ts +15 -13
- package/src/Chat/ChatTokenizedText.tsx +1 -1
- package/src/ComplexSelector/ComplexSelector.doc.mjs +55 -6
- package/src/ComplexSelector/ComplexSelector.test.tsx +197 -6
- package/src/ComplexSelector/ComplexSelector.tsx +153 -32
- package/src/ComplexSelector/index.ts +3 -1
- package/src/Markdown/parser.test.ts +53 -0
- package/src/Markdown/parser.ts +53 -12
- package/src/PowerSearch/PowerSearch.test.tsx +48 -3
- package/src/PowerSearch/PowerSearch.tsx +4 -1
- package/src/PowerSearch/formatFilterValue.test.ts +22 -0
- package/src/PowerSearch/formatFilterValue.ts +2 -4
- package/src/Table/Table.test.tsx +6 -0
- package/src/Table/columnUtils.ts +3 -1
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +72 -0
- package/src/TextArea/TextArea.tsx +26 -8
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/TreeList/TreeList.tsx +1 -1
- package/src/theme/defineTheme.test.ts +127 -0
- package/src/theme/defineTheme.ts +48 -51
- package/src/theme/extensibleAxes.test.ts +365 -0
- package/src/theme/mergeComponents.ts +59 -0
- package/src/theme/onMediaTokens.ts +9 -2
- package/src/utils/characters.test.ts +141 -0
- package/src/utils/characters.ts +88 -0
- package/src/utils/index.ts +2 -0
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file ComplexSelector.tsx
|
|
7
|
-
* @input Uses React, StyleX, Field, usePopover
|
|
8
|
-
* @output Exports
|
|
7
|
+
* @input Uses React, StyleX, Field, Icon slots, Layer positioning, and usePopover
|
|
8
|
+
* @output Exports a rich-selector shell with exact token-sized input and ghost triggers, plus an imperative open/close handle
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update:
|
|
@@ -13,17 +13,18 @@
|
|
|
13
13
|
* - /packages/core/src/ComplexSelector/ComplexSelector.test.tsx
|
|
14
14
|
* - /packages/core/src/ComplexSelector/index.ts
|
|
15
15
|
* - /apps/storybook/stories/ComplexSelector.stories.tsx
|
|
16
|
+
* - /packages/cli/assets/templates/blocks/components/ComplexSelector/ (showcase blocks)
|
|
16
17
|
*/
|
|
17
|
-
import React, { useCallback, useId, useOptimistic, useTransition } from 'react';
|
|
18
|
+
import React, { useCallback, useId, useImperativeHandle, useOptimistic, useRef, useTransition } from 'react';
|
|
18
19
|
import * as stylex from '@stylexjs/stylex';
|
|
19
20
|
import { Field, inputWrapperStyles } from "../Field/index.js";
|
|
20
|
-
import { Icon } from "../Icon/index.js";
|
|
21
|
+
import { Icon, renderIconSlot } from "../Icon/index.js";
|
|
21
22
|
import { Spinner } from "../Spinner/index.js";
|
|
22
23
|
import { useTranslator } from "../i18n/index.js";
|
|
23
24
|
import { layerAnimations } from "../Layer/layerAnimations.stylex.js";
|
|
24
25
|
import { usePopover } from "../Popover/usePopover.js";
|
|
25
26
|
import "../theme/tokens.stylex.js";
|
|
26
|
-
import { colorVars, durationVars, easeVars, radiusVars, sizeVars, spacingVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js";
|
|
27
|
+
import { colorVars, durationVars, easeVars, fontWeightVars, radiusVars, sizeVars, spacingVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js";
|
|
27
28
|
import { mergeProps } from "../utils/index.js";
|
|
28
29
|
import { composeEventHandlers } from "../utils/composeEventHandlers.js";
|
|
29
30
|
import { focusOutlineStyles } from "../utils/focusOutline.stylex.js";
|
|
@@ -82,6 +83,22 @@ const styles = {
|
|
|
82
83
|
kMwMTN: "xv1l7n4",
|
|
83
84
|
$$css: true
|
|
84
85
|
},
|
|
86
|
+
triggerGhost: {
|
|
87
|
+
kzqmXN: "x14atkfc",
|
|
88
|
+
kMzoRj: "xc342km",
|
|
89
|
+
kWkggS: "xjbqb8w",
|
|
90
|
+
kKwaWg: "x1ilzqfv xq8i9tn",
|
|
91
|
+
kGVxlE: "x1gnnqk1 x1irk71m x15ysqaf",
|
|
92
|
+
k63SB2: "x1e4wzip",
|
|
93
|
+
k1ekBW: "xrafxwg",
|
|
94
|
+
k3aq6I: "x3oybdh xk4oym4",
|
|
95
|
+
$$css: true
|
|
96
|
+
},
|
|
97
|
+
triggerGhostDisabled: {
|
|
98
|
+
kKwaWg: "x18o3ruo",
|
|
99
|
+
k3aq6I: "x1c071of x1pdlv7q",
|
|
100
|
+
$$css: true
|
|
101
|
+
},
|
|
85
102
|
triggerIcon: {
|
|
86
103
|
kmuXW: "x2lah0s",
|
|
87
104
|
$$css: true
|
|
@@ -109,15 +126,15 @@ const styles = {
|
|
|
109
126
|
$$css: true
|
|
110
127
|
},
|
|
111
128
|
sm: {
|
|
112
|
-
|
|
129
|
+
kZKoxP: "x6k0iem",
|
|
113
130
|
$$css: true
|
|
114
131
|
},
|
|
115
132
|
md: {
|
|
116
|
-
|
|
133
|
+
kZKoxP: "x1ueg155",
|
|
117
134
|
$$css: true
|
|
118
135
|
},
|
|
119
136
|
lg: {
|
|
120
|
-
|
|
137
|
+
kZKoxP: "xssyfek",
|
|
121
138
|
$$css: true
|
|
122
139
|
},
|
|
123
140
|
disabled: {
|
|
@@ -125,6 +142,16 @@ const styles = {
|
|
|
125
142
|
$$css: true
|
|
126
143
|
}
|
|
127
144
|
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Imperative control surface for ComplexSelector, accessed via the `handleRef`
|
|
148
|
+
* prop. Methods drive the same popover machinery as the built-in trigger, so
|
|
149
|
+
* they respect focus restoration, light dismiss, and Escape. Prefer these
|
|
150
|
+
* callbacks over mirroring open state in the parent — the selector owns its
|
|
151
|
+
* visibility, and imperative calls avoid the focus-management pitfalls of
|
|
152
|
+
* syncing an external `isOpen` prop.
|
|
153
|
+
*/
|
|
154
|
+
|
|
128
155
|
/**
|
|
129
156
|
* A selector shell for rich, custom selection surfaces.
|
|
130
157
|
*
|
|
@@ -170,8 +197,12 @@ export function ComplexSelector({
|
|
|
170
197
|
statusVariant = 'attached',
|
|
171
198
|
labelTooltip,
|
|
172
199
|
size = 'md',
|
|
200
|
+
variant = 'input',
|
|
201
|
+
startIcon,
|
|
173
202
|
width,
|
|
174
203
|
placement = 'below',
|
|
204
|
+
alignment = 'start',
|
|
205
|
+
handleRef,
|
|
175
206
|
contentXstyle,
|
|
176
207
|
xstyle,
|
|
177
208
|
className,
|
|
@@ -182,28 +213,62 @@ export function ComplexSelector({
|
|
|
182
213
|
}) {
|
|
183
214
|
const t = useTranslator();
|
|
184
215
|
const placeholder = placeholderFromProps ?? t('@astryx.selector.placeholder');
|
|
216
|
+
const effectiveStatusVariant = variant === 'ghost' && statusVariant === 'attached' ? 'detached' : statusVariant;
|
|
185
217
|
const triggerId = useId();
|
|
186
218
|
const labelId = useId();
|
|
187
219
|
const contentId = useId();
|
|
188
220
|
const descriptionId = useId();
|
|
189
221
|
const statusMessageId = useId();
|
|
190
222
|
const ariaDescribedBy = [description ? descriptionId : null, status?.message ? statusMessageId : null].filter(id => id != null).join(' ') || undefined;
|
|
223
|
+
const triggerRef = useRef(null);
|
|
224
|
+
const lastHideTimeRef = useRef(0);
|
|
191
225
|
const [isPending, startTransition] = useTransition();
|
|
192
226
|
const [optimisticValue, setOptimisticValue] = useOptimistic(value);
|
|
193
227
|
const isBusy = isLoading || isPending;
|
|
228
|
+
const handlePopoverHide = useCallback(() => {
|
|
229
|
+
lastHideTimeRef.current = Date.now();
|
|
230
|
+
triggerRef.current?.focus();
|
|
231
|
+
}, []);
|
|
194
232
|
const popover = usePopover({
|
|
195
233
|
dialogLabel: label,
|
|
196
234
|
hasCloseButton: false,
|
|
197
235
|
hasAutoFocus: true,
|
|
198
|
-
// The popup's theme target belongs on the SURFACE — the element painting
|
|
199
|
-
// background, radius and elevation — which `usePopover` owns. Rendered on
|
|
200
|
-
// the content box below it, a theme's background or radius rule paints the
|
|
201
|
-
// wrong box.
|
|
202
236
|
surfaceTarget: 'complex-selector-popup',
|
|
203
|
-
onHide:
|
|
204
|
-
document.getElementById(triggerId)?.focus();
|
|
205
|
-
}
|
|
237
|
+
onHide: handlePopoverHide
|
|
206
238
|
});
|
|
239
|
+
const isOpen = popover.isOpen;
|
|
240
|
+
const handleTriggerClick = useCallback(() => {
|
|
241
|
+
if (isDisabled || Date.now() - lastHideTimeRef.current < 50) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (popover.isOpen) {
|
|
245
|
+
popover.hide();
|
|
246
|
+
} else {
|
|
247
|
+
popover.show();
|
|
248
|
+
}
|
|
249
|
+
}, [isDisabled, popover]);
|
|
250
|
+
const close = useCallback(() => {
|
|
251
|
+
popover.hide();
|
|
252
|
+
}, [popover]);
|
|
253
|
+
useImperativeHandle(handleRef, () => ({
|
|
254
|
+
open: () => {
|
|
255
|
+
if (!isDisabled) {
|
|
256
|
+
popover.show();
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
close: () => popover.hide(),
|
|
260
|
+
toggle: () => {
|
|
261
|
+
if (isDisabled) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (popover.isOpen) {
|
|
265
|
+
popover.hide();
|
|
266
|
+
} else {
|
|
267
|
+
popover.show();
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
isOpen: () => popover.isOpen
|
|
271
|
+
}), [isDisabled, popover]);
|
|
207
272
|
const commitValue = useCallback(nextValue => {
|
|
208
273
|
onChange?.(nextValue);
|
|
209
274
|
if (changeAction) {
|
|
@@ -217,8 +282,8 @@ export function ComplexSelector({
|
|
|
217
282
|
const content = /*#__PURE__*/_jsx("div", {
|
|
218
283
|
id: contentId,
|
|
219
284
|
...stylex.props(styles.content, contentXstyle),
|
|
220
|
-
children: children(optimisticValue, commitValue,
|
|
221
|
-
isOpen
|
|
285
|
+
children: children(optimisticValue, commitValue, close, {
|
|
286
|
+
isOpen,
|
|
222
287
|
isBusy,
|
|
223
288
|
triggerId,
|
|
224
289
|
contentId
|
|
@@ -229,25 +294,21 @@ export function ComplexSelector({
|
|
|
229
294
|
ref: popover.triggerRef,
|
|
230
295
|
"data-testid": testId,
|
|
231
296
|
...props,
|
|
232
|
-
onClick: composeEventHandlers(onClickProp,
|
|
233
|
-
if (!isDisabled) {
|
|
234
|
-
popover.toggle();
|
|
235
|
-
}
|
|
236
|
-
}),
|
|
297
|
+
onClick: composeEventHandlers(onClickProp, handleTriggerClick),
|
|
237
298
|
...mergeProps(themeProps('complex-selector', {
|
|
299
|
+
variant,
|
|
238
300
|
size,
|
|
239
301
|
status: status?.type ?? null
|
|
240
|
-
}), stylex.props(inputWrapperStyles.base, styles.triggerContainer, styles[size],
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
children: [/*#__PURE__*/_jsx("button", {
|
|
302
|
+
}), stylex.props(inputWrapperStyles.base, styles.triggerContainer, styles[size], variant === 'input' && focusOutlineStyles.focusWithin, variant === 'ghost' && styles.triggerGhost, variant === 'ghost' && focusOutlineStyles.focusWithin, isDisabled && inputWrapperStyles.disabled, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, isDisabled && styles.disabled, triggerLabel == null && styles.placeholder, xstyle), className, style),
|
|
303
|
+
children: [startIcon && renderIconSlot(startIcon, {
|
|
304
|
+
size: 'sm',
|
|
305
|
+
color: 'secondary'
|
|
306
|
+
}), /*#__PURE__*/_jsx("button", {
|
|
307
|
+
ref: triggerRef,
|
|
247
308
|
id: triggerId,
|
|
248
309
|
type: "button",
|
|
249
310
|
"aria-haspopup": "dialog",
|
|
250
|
-
"aria-expanded":
|
|
311
|
+
"aria-expanded": isOpen,
|
|
251
312
|
"aria-controls": contentId,
|
|
252
313
|
"aria-describedby": ariaDescribedBy,
|
|
253
314
|
"aria-labelledby": labelId,
|
|
@@ -256,7 +317,7 @@ export function ComplexSelector({
|
|
|
256
317
|
"aria-busy": isBusy || undefined,
|
|
257
318
|
disabled: isDisabled,
|
|
258
319
|
onKeyDown: event => {
|
|
259
|
-
if (event.key === 'ArrowDown' && !
|
|
320
|
+
if (event.key === 'ArrowDown' && !isOpen && !isDisabled) {
|
|
260
321
|
event.preventDefault();
|
|
261
322
|
popover.show();
|
|
262
323
|
}
|
|
@@ -281,14 +342,14 @@ export function ComplexSelector({
|
|
|
281
342
|
// IS the trigger's icon element — one node carrying the box, the
|
|
282
343
|
// color, the rotation, and the theme target.
|
|
283
344
|
,
|
|
284
|
-
xstyle: [styles.triggerIcon, styles.triggerIconRotation,
|
|
345
|
+
xstyle: [styles.triggerIcon, styles.triggerIconRotation, isOpen && styles.triggerIconOpen],
|
|
285
346
|
...themeProps('complex-selector-indicator-icon', {
|
|
286
|
-
state:
|
|
347
|
+
state: isOpen ? 'expanded' : 'collapsed'
|
|
287
348
|
})
|
|
288
349
|
})]
|
|
289
350
|
}), popover.render(content, {
|
|
290
351
|
placement,
|
|
291
|
-
alignment
|
|
352
|
+
alignment,
|
|
292
353
|
offset: spacingVars['--spacing-1'],
|
|
293
354
|
xstyle: [styles.popover, layerAnimations[placement]]
|
|
294
355
|
})]
|
|
@@ -308,7 +369,7 @@ export function ComplexSelector({
|
|
|
308
369
|
message: status.message,
|
|
309
370
|
messageID: status.message ? statusMessageId : undefined
|
|
310
371
|
} : undefined,
|
|
311
|
-
statusVariant:
|
|
372
|
+
statusVariant: effectiveStatusVariant,
|
|
312
373
|
labelTooltip: labelTooltip,
|
|
313
374
|
width: width,
|
|
314
375
|
children: selectorContent
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file index.ts
|
|
3
|
-
* @output Exports ComplexSelector and types
|
|
3
|
+
* @output Exports ComplexSelector and its trigger-size contract types
|
|
4
4
|
* @position Public API entry point
|
|
5
5
|
*/
|
|
6
|
-
export { ComplexSelector, type ComplexSelectorProps, type ComplexSelectorRenderState, type ComplexSelectorSize, type ComplexSelectorStatus, } from './ComplexSelector';
|
|
6
|
+
export { ComplexSelector, type ComplexSelectorHandle, type ComplexSelectorProps, type ComplexSelectorRenderState, type ComplexSelectorSize, type ComplexSelectorStatus, type ComplexSelectorVariant, } from './ComplexSelector';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ComplexSelector/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ComplexSelector/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAMH,MAAM,MAAM,UAAU,GAClB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC/B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACtC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACxC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GAC/C;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC/B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACpD;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,GACzC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,GACpC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAC,CAAC;AAEpB,MAAM,MAAM,SAAS,GACjB;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACvE;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GAC3C;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GACtD;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAA;CAAC,GAC3C;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,IAAI,EAAE,aAAa,EAAE,EAAE,CAAC;CACzB,GACD;IAAC,IAAI,EAAE,IAAI,CAAA;CAAC,GACZ;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAA;CAAC,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;AAMhE;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;+DAC2D;IAC3D,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/Markdown/parser.js
CHANGED
|
@@ -203,7 +203,7 @@ function matchReferenceLink(text, start, linkDefs, opts) {
|
|
|
203
203
|
// matches nothing.
|
|
204
204
|
const label = rawLabel === '' ? linkText : rawLabel;
|
|
205
205
|
const href = linkDefs.get(normalizeLinkLabel(label));
|
|
206
|
-
if (href != null) {
|
|
206
|
+
if (href != null && isSafeUrl(href)) {
|
|
207
207
|
return {
|
|
208
208
|
node: {
|
|
209
209
|
type: 'link',
|
|
@@ -222,7 +222,7 @@ function matchReferenceLink(text, start, linkDefs, opts) {
|
|
|
222
222
|
return null;
|
|
223
223
|
}
|
|
224
224
|
const href = linkDefs.get(normalizeLinkLabel(linkText));
|
|
225
|
-
if (href == null) {
|
|
225
|
+
if (href == null || !isSafeUrl(href)) {
|
|
226
226
|
return null;
|
|
227
227
|
}
|
|
228
228
|
return {
|
|
@@ -304,6 +304,27 @@ function isWordChar(ch) {
|
|
|
304
304
|
return /\w/.test(ch);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// URL scheme sanitization
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Reject URLs with dangerous schemes (javascript:, vbscript:, data:) that
|
|
313
|
+
* could execute arbitrary code when rendered as link hrefs or image srcs.
|
|
314
|
+
* Returns true if the URL is safe to use, false otherwise.
|
|
315
|
+
*/
|
|
316
|
+
function isSafeUrl(url) {
|
|
317
|
+
// Trim and collapse whitespace/control chars that browsers tolerate but
|
|
318
|
+
// could bypass a naive prefix check (e.g. "java\nscript:alert(1)").
|
|
319
|
+
// eslint-disable-next-line no-control-regex -- control chars are the bypass
|
|
320
|
+
const normalized = url.replace(/[\x00-\x1f\x7f]/g, '').trim();
|
|
321
|
+
const lower = normalized.toLowerCase();
|
|
322
|
+
if (lower.startsWith('javascript:') || lower.startsWith('vbscript:') || lower.startsWith('data:text/html')) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
|
|
307
328
|
// ---------------------------------------------------------------------------
|
|
308
329
|
// Inline parser
|
|
309
330
|
// ---------------------------------------------------------------------------
|
|
@@ -419,11 +440,20 @@ function parseInlineImpl(text, opts) {
|
|
|
419
440
|
if (altClose !== -1 && text[altClose + 1] === '(') {
|
|
420
441
|
const srcClose = findClosingParen(text, altClose + 2);
|
|
421
442
|
if (srcClose !== -1) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
443
|
+
const src = text.slice(altClose + 2, srcClose);
|
|
444
|
+
if (!isSafeUrl(src)) {
|
|
445
|
+
// Dangerous scheme — emit as plain text.
|
|
446
|
+
nodes.push({
|
|
447
|
+
type: 'text',
|
|
448
|
+
content: text.slice(i, srcClose + 1)
|
|
449
|
+
});
|
|
450
|
+
} else {
|
|
451
|
+
nodes.push({
|
|
452
|
+
type: 'image',
|
|
453
|
+
src,
|
|
454
|
+
alt: text.slice(i + 2, altClose)
|
|
455
|
+
});
|
|
456
|
+
}
|
|
427
457
|
i = srcClose + 1;
|
|
428
458
|
continue;
|
|
429
459
|
}
|
|
@@ -459,11 +489,20 @@ function parseInlineImpl(text, opts) {
|
|
|
459
489
|
if (textClose !== -1 && text[textClose + 1] === '(') {
|
|
460
490
|
const urlClose = findClosingParen(text, textClose + 2);
|
|
461
491
|
if (urlClose !== -1) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
492
|
+
const href = text.slice(textClose + 2, urlClose);
|
|
493
|
+
if (!isSafeUrl(href)) {
|
|
494
|
+
// Dangerous scheme — emit as plain text instead of a link.
|
|
495
|
+
nodes.push({
|
|
496
|
+
type: 'text',
|
|
497
|
+
content: text.slice(i, urlClose + 1)
|
|
498
|
+
});
|
|
499
|
+
} else {
|
|
500
|
+
nodes.push({
|
|
501
|
+
type: 'link',
|
|
502
|
+
href,
|
|
503
|
+
children: parseInlineImpl(text.slice(i + 1, textClose), opts)
|
|
504
|
+
});
|
|
505
|
+
}
|
|
467
506
|
i = urlClose + 1;
|
|
468
507
|
continue;
|
|
469
508
|
}
|
|
@@ -690,6 +729,10 @@ function scanAutolinksInText(text) {
|
|
|
690
729
|
let m;
|
|
691
730
|
while ((m = re.exec(text)) !== null) {
|
|
692
731
|
const url = m[1];
|
|
732
|
+
// Skip dangerous URL schemes (javascript:, vbscript:, data:text/html)
|
|
733
|
+
if (!isSafeUrl(url)) {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
693
736
|
matches.push({
|
|
694
737
|
start: m.index,
|
|
695
738
|
end: m.index + m[0].length,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PowerSearch.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/PowerSearch.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,cAAc,CAAC;AAMtB,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAC;AAEtC,OAAO,KAAK,EAAC,WAAW,EAAE,kBAAkB,EAAC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"PowerSearch.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/PowerSearch.tsx"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,7 @@ import { formatFilterValue } from "./formatFilterValue.js";
|
|
|
33
33
|
import { PowerSearchEditPopover } from "./PowerSearchEditPopover.js";
|
|
34
34
|
import { resolveOperatorLabel } from "./resolveOperatorLabel.js";
|
|
35
35
|
import { themeProps } from "../utils/themeProps.js";
|
|
36
|
+
import { truncateCharacters } from "../utils/characters.js";
|
|
36
37
|
import { useTranslator } from "../i18n/index.js";
|
|
37
38
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
38
39
|
// =============================================================================
|
|
@@ -68,7 +69,9 @@ const popoverLayerStyles = {
|
|
|
68
69
|
}
|
|
69
70
|
};
|
|
70
71
|
function truncateString(value, limit) {
|
|
71
|
-
|
|
72
|
+
// Same semantics as before — strings within limit + 3 pass through, longer
|
|
73
|
+
// ones cut to limit + '...' — but counted in characters, not code units.
|
|
74
|
+
return truncateCharacters(value, limit + 3, '...');
|
|
72
75
|
}
|
|
73
76
|
function getEnumLabel(values, value) {
|
|
74
77
|
return values.find(v => v.value === value)?.label ?? value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatFilterValue.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/formatFilterValue.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAE,WAAW,EAAW,MAAM,SAAS,CAAC;AAClE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"formatFilterValue.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/formatFilterValue.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAE,WAAW,EAAW,MAAM,SAAS,CAAC;AAClE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,SAAS,CAAC;AA4C1C,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,EACjB,CAAC,EAAE,YAAY,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAsHR"}
|
|
@@ -10,11 +10,9 @@
|
|
|
10
10
|
* - /packages/core/src/PowerSearch/index.ts
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { truncateCharacters } from "../utils/characters.js";
|
|
13
14
|
function truncate(str, maxLength) {
|
|
14
|
-
|
|
15
|
-
return str;
|
|
16
|
-
}
|
|
17
|
-
return str.slice(0, maxLength - 1) + '\u2026';
|
|
15
|
+
return truncateCharacters(str, maxLength);
|
|
18
16
|
}
|
|
19
17
|
function formatEnumLabel(value, enumValues) {
|
|
20
18
|
const item = enumValues.find(v => v.value === value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columnUtils.d.ts","sourceRoot":"","sources":["../../src/Table/columnUtils.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAE,SAAS,EAAC,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAC,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"columnUtils.d.ts","sourceRoot":"","sources":["../../src/Table/columnUtils.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAE,SAAS,EAAC,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAC,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAC,MAAM,SAAS,CAAC;AAGxE,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAM5C;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,KAAK,EAAE,aAAa,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC1C,oGAAoG;IACpG,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,GACxB,oBAAoB,CAmEtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,KAAK,GAAE,MAAU,EACjB,OAAO,CAAC,EAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,GAC5B,iBAAiB,CAMnB;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAE/C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,MAAM,GACV,SAAS,CAiBX;AA+DD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,IAAI,EAAE,CAAC,EAAE,GACR,WAAW,CAAC,CAAC,CAAC,EAAE,CAmDlB"}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* - /packages/core/src/Table/index.ts (exports if functions change)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { firstCharacter } from "../utils/characters.js";
|
|
15
|
+
|
|
14
16
|
/** Default minimum width (in px) for proportional columns. */
|
|
15
17
|
export const DEFAULT_MIN_COLUMN_WIDTH = 120;
|
|
16
18
|
|
|
@@ -153,7 +155,8 @@ export function capitalize(str) {
|
|
|
153
155
|
if (str.length === 0) {
|
|
154
156
|
return str;
|
|
155
157
|
}
|
|
156
|
-
|
|
158
|
+
const first = firstCharacter(str);
|
|
159
|
+
return first.toUpperCase() + str.slice(first.length);
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
/**
|
|
@@ -149,10 +149,13 @@ export interface TextAreaProps extends Omit<BaseProps, 'onChange' | 'defaultValu
|
|
|
149
149
|
*/
|
|
150
150
|
onPaste?: (e: ClipboardEvent<HTMLTextAreaElement>) => void;
|
|
151
151
|
/**
|
|
152
|
-
* Maximum number of characters allowed
|
|
153
|
-
*
|
|
152
|
+
* Maximum number of characters allowed, counted as user-perceived
|
|
153
|
+
* characters — an emoji or flag sequence counts as one. When set,
|
|
154
|
+
* displays a character counter below the textarea.
|
|
154
155
|
* Does not enforce the limit natively — the counter shows error styling
|
|
155
|
-
* when exceeded, and the consumer can validate via onChange.
|
|
156
|
+
* when exceeded, and the consumer can validate via onChange. Validate with
|
|
157
|
+
* `characterCount` (exported from this package) rather than `value.length`
|
|
158
|
+
* so enforcement matches the displayed count.
|
|
156
159
|
*/
|
|
157
160
|
maxLength?: number;
|
|
158
161
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../src/TextArea/TextArea.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../src/TextArea/TextArea.tsx"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - /apps/storybook/stories/TextArea.stories.tsx (storybook stories)
|
|
16
16
|
* - /packages/cli/assets/templates/blocks/components/TextArea/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
|
-
import { useId, useOptimistic, useTransition, useRef, useCallback } from 'react';
|
|
18
|
+
import { useId, useOptimistic, useTransition, useRef, useCallback, useMemo } from 'react';
|
|
19
19
|
import * as stylex from '@stylexjs/stylex';
|
|
20
20
|
import "../theme/tokens.stylex.js";
|
|
21
21
|
import { colorVars, spacingVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js";
|
|
@@ -29,6 +29,7 @@ import { useInputStatusIcon } from "../hooks/useInputStatusIcon.js";
|
|
|
29
29
|
import { useAnnounce } from "../hooks/useAnnounce.js";
|
|
30
30
|
import { useSize } from "../SizeContext/SizeContext.js";
|
|
31
31
|
import { themeProps } from "../utils/themeProps.js";
|
|
32
|
+
import { characterCount } from "../utils/characters.js";
|
|
32
33
|
import { useTranslator } from "../i18n/index.js";
|
|
33
34
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
34
35
|
const COUNTER_WARNING_THRESHOLD = 0.8;
|
|
@@ -208,7 +209,11 @@ export function TextArea({
|
|
|
208
209
|
return;
|
|
209
210
|
}
|
|
210
211
|
const newValue = e.target.value;
|
|
211
|
-
announceCounter
|
|
212
|
+
// Guarded here, not just inside announceCounter, so textareas without a
|
|
213
|
+
// maxLength never pay for segmenting the whole value on each keystroke.
|
|
214
|
+
if (maxLength != null) {
|
|
215
|
+
announceCounter(characterCount(newValue));
|
|
216
|
+
}
|
|
212
217
|
onChange?.(newValue, e);
|
|
213
218
|
if (changeAction && !e.defaultPrevented) {
|
|
214
219
|
startTransition(async () => {
|
|
@@ -217,6 +222,12 @@ export function TextArea({
|
|
|
217
222
|
});
|
|
218
223
|
}
|
|
219
224
|
};
|
|
225
|
+
|
|
226
|
+
// Counter semantics count user-perceived characters, so an emoji or flag
|
|
227
|
+
// sequence counts as one character, not its code units.
|
|
228
|
+
// Only measured when a counter exists — segmentation is O(value length),
|
|
229
|
+
// and memoized so re-renders that keep the same value skip it entirely.
|
|
230
|
+
const valueLength = useMemo(() => maxLength != null ? characterCount(optimisticValue) : 0, [maxLength, optimisticValue]);
|
|
220
231
|
const effectivelyDisabled = isDisabled || isBusy;
|
|
221
232
|
|
|
222
233
|
// Focus textarea when clicking anywhere on the wrapper (icons, padding, etc.)
|
|
@@ -293,7 +304,7 @@ export function TextArea({
|
|
|
293
304
|
"data-autofocus": hasAutoFocus || undefined,
|
|
294
305
|
"aria-describedby": ariaDescribedBy,
|
|
295
306
|
"aria-required": isRequired && !isOptional ? 'true' : undefined,
|
|
296
|
-
"aria-invalid": status?.type === 'error' || maxLength != null &&
|
|
307
|
+
"aria-invalid": status?.type === 'error' || maxLength != null && valueLength > maxLength ? 'true' : undefined,
|
|
297
308
|
"aria-busy": isBusy || undefined,
|
|
298
309
|
...stylex.props(styles.textarea, textareaSizeStyles[size], isDisabled && styles.textareaDisabled, Boolean(startIcon) && styles.textareaWithStartIcon,
|
|
299
310
|
// Reserve trailing space only when the end slot actually renders
|
|
@@ -318,8 +329,8 @@ export function TextArea({
|
|
|
318
329
|
1: {
|
|
319
330
|
className: "x10l6tqk x112dmjn x13oyzgk x47corl x78zum5 x6s0dn4 xzye2dw x9ynric x141an7d x1ltkj2j xjt36v0"
|
|
320
331
|
}
|
|
321
|
-
}[!!(
|
|
322
|
-
children: [
|
|
332
|
+
}[!!(valueLength > maxLength) << 0],
|
|
333
|
+
children: [valueLength > maxLength &&
|
|
323
334
|
/*#__PURE__*/
|
|
324
335
|
// Non-color cue so the over-limit state isn't conveyed by the red
|
|
325
336
|
// color alone (WCAG 1.4.1). Decorative — the count text and the
|
|
@@ -327,7 +338,7 @@ export function TextArea({
|
|
|
327
338
|
_jsx(Icon, {
|
|
328
339
|
icon: "warning",
|
|
329
340
|
size: "sm"
|
|
330
|
-
}),
|
|
341
|
+
}), valueLength, "/", maxLength]
|
|
331
342
|
})]
|
|
332
343
|
}), showsDisabledMessage && disabledMessageTooltip.renderTooltip(disabledMessage)]
|
|
333
344
|
});
|
|
@@ -226,7 +226,8 @@ export function TreeList({
|
|
|
226
226
|
ref: ref,
|
|
227
227
|
"data-testid": testId,
|
|
228
228
|
...mergeProps(themeProps('tree-list', {
|
|
229
|
-
density
|
|
229
|
+
density,
|
|
230
|
+
variant
|
|
230
231
|
}), stylex.props(styles.root, xstyle), className, style),
|
|
231
232
|
children: [header != null && /*#__PURE__*/_jsx("div", {
|
|
232
233
|
id: headerId,
|