@gcsa/console-react 0.1.2 → 0.1.4
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/GcsaCascader.js +19 -18
- package/dist/GcsaCascader.js.map +1 -1
- package/dist/GcsaConfigProvider.js +38 -36
- package/dist/GcsaConfigProvider.js.map +1 -1
- package/dist/GcsaPopover.d.ts +3 -1
- package/dist/GcsaPopover.js +30 -29
- package/dist/GcsaPopover.js.map +1 -1
- package/dist/GcsaSelect.js +90 -81
- package/dist/GcsaSelect.js.map +1 -1
- package/dist/GcsaTransfer.js +72 -57
- package/dist/GcsaTransfer.js.map +1 -1
- package/dist/GcsaUpload.js +1 -1
- package/dist/GcsaUpload.js.map +1 -1
- package/dist/icons/iconNames.d.ts +2 -2
- package/dist/icons/iconNames.js +1 -0
- package/dist/icons/iconNames.js.map +1 -1
- package/dist/icons/registry.d.ts +1 -1
- package/dist/icons/registry.js +38 -35
- package/dist/icons/registry.js.map +1 -1
- package/dist/icons/svg/result-empty.svg.js +12 -2
- package/dist/icons/svg/result-empty.svg.js.map +1 -1
- package/dist/icons/svg/upload.svg.js +10 -0
- package/dist/icons/svg/upload.svg.js.map +1 -0
- package/dist/icons/svg/users.svg.js.map +1 -1
- package/dist/style.css +223 -140
- package/package.json +3 -3
- package/src/GcsaCascader.tsx +1 -0
- package/src/GcsaConfigProvider.tsx +6 -1
- package/src/GcsaPopover.tsx +4 -1
- package/src/GcsaSelect.tsx +20 -2
- package/src/GcsaTransfer.tsx +41 -13
- package/src/GcsaUpload.tsx +1 -1
- package/src/icons/iconNames.ts +1 -0
- package/src/icons/registry.ts +3 -1
- package/src/icons/svg/result-empty.svg +11 -1
- package/src/icons/svg/upload.svg +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gcsa/console-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "GCSA console UI — React components (Ark UI + TanStack + GCSA styles)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"@tanstack/react-form": "^1.12.2",
|
|
35
35
|
"@tanstack/react-table": "^8.21.3",
|
|
36
36
|
"zod": "^3.24.4",
|
|
37
|
-
"@gcsa/console-
|
|
38
|
-
"@gcsa/console-
|
|
37
|
+
"@gcsa/console-core": "0.1.4",
|
|
38
|
+
"@gcsa/console-styles": "0.1.4"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18.0.0 || ^19.0.0",
|
package/src/GcsaCascader.tsx
CHANGED
|
@@ -38,6 +38,7 @@ export function GcsaConfigProvider({
|
|
|
38
38
|
const restoreDocumentTheme = useContext(GcsaRestoreDocumentThemeContext);
|
|
39
39
|
const [toasts, setToasts] = useState<Array<{ id: number; message: string; type: GcsaMessageType }>>([]);
|
|
40
40
|
const [toastZIndex, setToastZIndex] = useState<number>(() => readToastZIndex());
|
|
41
|
+
const [portalMounted, setPortalMounted] = useState(false);
|
|
41
42
|
const idRef = useRef(0);
|
|
42
43
|
const MAX_TOASTS = 4;
|
|
43
44
|
|
|
@@ -77,6 +78,10 @@ export function GcsaConfigProvider({
|
|
|
77
78
|
refreshToastZIndex();
|
|
78
79
|
}, []);
|
|
79
80
|
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
setPortalMounted(true);
|
|
83
|
+
}, []);
|
|
84
|
+
|
|
80
85
|
useEffect(() => {
|
|
81
86
|
if (!shouldSyncDocument) return;
|
|
82
87
|
return () => {
|
|
@@ -99,7 +104,7 @@ export function GcsaConfigProvider({
|
|
|
99
104
|
<div className="gcsa-console-root" data-gcsa-theme={theme}>
|
|
100
105
|
{children}
|
|
101
106
|
</div>
|
|
102
|
-
{
|
|
107
|
+
{portalMounted
|
|
103
108
|
? createPortal(
|
|
104
109
|
<div
|
|
105
110
|
className="gcsa-portal-scope gcsa-toast-portal"
|
package/src/GcsaPopover.tsx
CHANGED
|
@@ -22,6 +22,8 @@ export type GcsaPopoverProps = {
|
|
|
22
22
|
/** 受控开关;不传则为非受控 */
|
|
23
23
|
open?: boolean;
|
|
24
24
|
onOpenChange?: (open: boolean) => void;
|
|
25
|
+
/** 启用 size middleware,写入 --reference-width */
|
|
26
|
+
fitViewport?: boolean;
|
|
25
27
|
/** 预设尺寸;与 minWidth / maxWidth / width 可同时使用,inline 优先生效 */
|
|
26
28
|
size?: GcsaPopoverSize;
|
|
27
29
|
/** 面板宽度,如 560 或 '560px' */
|
|
@@ -46,6 +48,7 @@ export function GcsaPopover({
|
|
|
46
48
|
gutter = 8,
|
|
47
49
|
open,
|
|
48
50
|
onOpenChange,
|
|
51
|
+
fitViewport = false,
|
|
49
52
|
size = 'default',
|
|
50
53
|
width,
|
|
51
54
|
minWidth,
|
|
@@ -107,7 +110,7 @@ export function GcsaPopover({
|
|
|
107
110
|
open={resolvedOpen}
|
|
108
111
|
lazyMount
|
|
109
112
|
unmountOnExit
|
|
110
|
-
positioning={createGcsaPopupPositioning({ placement, gutter })}
|
|
113
|
+
positioning={createGcsaPopupPositioning({ placement, gutter, fitViewport })}
|
|
111
114
|
onOpenChange={details => handleOpenChange(details.open)}
|
|
112
115
|
>
|
|
113
116
|
<Popover.Trigger asChild>{triggerNode}</Popover.Trigger>
|
package/src/GcsaSelect.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, useRef, type CSSProperties } from 'react';
|
|
1
|
+
import { useMemo, useRef, useState, type CSSProperties } from 'react';
|
|
2
2
|
import { createListCollection } from '@ark-ui/react/collection';
|
|
3
3
|
import { Select } from '@ark-ui/react/select';
|
|
4
4
|
import { Portal } from '@ark-ui/react/portal';
|
|
@@ -94,6 +94,7 @@ export function GcsaSelect({
|
|
|
94
94
|
onValueChange
|
|
95
95
|
}: GcsaSelectProps) {
|
|
96
96
|
const controlRef = useRef<HTMLDivElement>(null);
|
|
97
|
+
const [triggerWidth, setTriggerWidth] = useState<number | undefined>();
|
|
97
98
|
const resolvedSize = useGcsaComponentSize(size);
|
|
98
99
|
const messages = useGcsaLocaleMessages();
|
|
99
100
|
const { zIndex, onOpenChange } = useGcsaPopupZIndex();
|
|
@@ -116,6 +117,13 @@ export function GcsaSelect({
|
|
|
116
117
|
[options, hasGroups, groupOrder]
|
|
117
118
|
);
|
|
118
119
|
|
|
120
|
+
const syncTriggerWidth = () => {
|
|
121
|
+
const width = controlRef.current?.getBoundingClientRect().width;
|
|
122
|
+
if (width) {
|
|
123
|
+
setTriggerWidth(Math.round(width));
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
119
127
|
const selectedValue = value == null ? [] : [value];
|
|
120
128
|
const showListHint = options.length >= listHintThreshold;
|
|
121
129
|
const listHintText = formatGcsaLocaleMessage(messages.selectListHint, { count: options.length });
|
|
@@ -123,7 +131,14 @@ export function GcsaSelect({
|
|
|
123
131
|
const isEmpty = options.length === 0;
|
|
124
132
|
const contentStyle = {
|
|
125
133
|
'--gcsa-select-list-max-height': `${listMaxHeight}px`,
|
|
126
|
-
...(zIndex == null ? {} : { zIndex })
|
|
134
|
+
...(zIndex == null ? {} : { zIndex }),
|
|
135
|
+
...(sameWidth && triggerWidth != null
|
|
136
|
+
? {
|
|
137
|
+
width: triggerWidth,
|
|
138
|
+
minWidth: triggerWidth,
|
|
139
|
+
'--reference-width': `${triggerWidth}px`
|
|
140
|
+
}
|
|
141
|
+
: {})
|
|
127
142
|
} as CSSProperties;
|
|
128
143
|
const positionerStyle = zIndex == null ? undefined : ({ zIndex } as CSSProperties);
|
|
129
144
|
const positioning = useMemo(
|
|
@@ -139,6 +154,9 @@ export function GcsaSelect({
|
|
|
139
154
|
lazyMount
|
|
140
155
|
unmountOnExit
|
|
141
156
|
onOpenChange={details => {
|
|
157
|
+
if (details.open && sameWidth) {
|
|
158
|
+
syncTriggerWidth();
|
|
159
|
+
}
|
|
142
160
|
onOpenChange(details.open);
|
|
143
161
|
preserveDrawerScrollFrom(controlRef.current);
|
|
144
162
|
}}
|
package/src/GcsaTransfer.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react';
|
|
1
|
+
import { useMemo, useState, type KeyboardEvent } from 'react';
|
|
2
2
|
import type { GcsaTransferOption } from '@gcsa/console-core';
|
|
3
|
+
import { GcsaCheckbox } from './GcsaCheckbox';
|
|
3
4
|
import { GcsaInput } from './GcsaInput';
|
|
4
5
|
import { useGcsaLocaleMessages } from './hooks/useGcsaLocale';
|
|
5
6
|
|
|
@@ -29,6 +30,7 @@ export function GcsaTransfer({
|
|
|
29
30
|
const [targetSelected, setTargetSelected] = useState<string[]>([]);
|
|
30
31
|
const [sourceQuery, setSourceQuery] = useState('');
|
|
31
32
|
const [targetQuery, setTargetQuery] = useState('');
|
|
33
|
+
|
|
32
34
|
const availableItems = useMemo(
|
|
33
35
|
() =>
|
|
34
36
|
options.filter(
|
|
@@ -38,6 +40,7 @@ export function GcsaTransfer({
|
|
|
38
40
|
),
|
|
39
41
|
[options, sourceQuery, value]
|
|
40
42
|
);
|
|
43
|
+
|
|
41
44
|
const targetItems = useMemo(
|
|
42
45
|
() =>
|
|
43
46
|
options.filter(
|
|
@@ -47,18 +50,28 @@ export function GcsaTransfer({
|
|
|
47
50
|
),
|
|
48
51
|
[options, targetQuery, value]
|
|
49
52
|
);
|
|
53
|
+
|
|
50
54
|
const update = (next: string[]) => {
|
|
51
55
|
onValueChange?.(next);
|
|
52
56
|
onChange?.(next);
|
|
53
57
|
setSourceSelected([]);
|
|
54
58
|
setTargetSelected([]);
|
|
55
59
|
};
|
|
56
|
-
|
|
60
|
+
|
|
61
|
+
const setSelected = (key: string, side: 'source' | 'target', checked: boolean) => {
|
|
62
|
+
const setter = side === 'source' ? setSourceSelected : setTargetSelected;
|
|
63
|
+
setter(current => {
|
|
64
|
+
if (checked) return current.includes(key) ? current : [...current, key];
|
|
65
|
+
return current.filter(item => item !== key);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const onItemActivate = (item: GcsaTransferOption, side: 'source' | 'target') => {
|
|
70
|
+
if (disabled || item.disabled) return;
|
|
57
71
|
const current = side === 'source' ? sourceSelected : targetSelected;
|
|
58
|
-
|
|
59
|
-
if (side === 'source') setSourceSelected(next);
|
|
60
|
-
else setTargetSelected(next);
|
|
72
|
+
setSelected(item.value, side, !current.includes(item.value));
|
|
61
73
|
};
|
|
74
|
+
|
|
62
75
|
const panel = (items: GcsaTransferOption[], title: string, side: 'source' | 'target') => (
|
|
63
76
|
<section className="gcsa-transfer__panel" aria-label={title}>
|
|
64
77
|
<header className="gcsa-transfer__header">
|
|
@@ -77,21 +90,35 @@ export function GcsaTransfer({
|
|
|
77
90
|
{items.length ? (
|
|
78
91
|
items.map(item => {
|
|
79
92
|
const selected = (side === 'source' ? sourceSelected : targetSelected).includes(item.value);
|
|
93
|
+
const itemDisabled = disabled || Boolean(item.disabled);
|
|
94
|
+
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
95
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
onItemActivate(item, side);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const onRowClick = () => onItemActivate(item, side);
|
|
80
101
|
return (
|
|
81
|
-
<
|
|
102
|
+
<div
|
|
82
103
|
key={item.value}
|
|
83
|
-
type="button"
|
|
84
104
|
className={`gcsa-transfer__item gcsa-focus-ring${selected ? ' gcsa-transfer__item--selected' : ''}`}
|
|
85
|
-
disabled={disabled || item.disabled}
|
|
86
105
|
role="option"
|
|
106
|
+
tabIndex={itemDisabled ? -1 : 0}
|
|
87
107
|
aria-selected={selected}
|
|
88
|
-
|
|
108
|
+
aria-disabled={itemDisabled || undefined}
|
|
109
|
+
onClick={onRowClick}
|
|
110
|
+
onKeyDown={onKeyDown}
|
|
89
111
|
>
|
|
90
|
-
<span className="gcsa-transfer__check"
|
|
91
|
-
|
|
112
|
+
<span className="gcsa-transfer__check-wrap" onClick={event => event.stopPropagation()}>
|
|
113
|
+
<GcsaCheckbox
|
|
114
|
+
checked={selected}
|
|
115
|
+
disabled={itemDisabled}
|
|
116
|
+
ariaLabel={item.label}
|
|
117
|
+
onCheckedChange={checked => setSelected(item.value, side, checked)}
|
|
118
|
+
/>
|
|
92
119
|
</span>
|
|
93
|
-
<span>{item.label}</span>
|
|
94
|
-
</
|
|
120
|
+
<span className="gcsa-transfer__label">{item.label}</span>
|
|
121
|
+
</div>
|
|
95
122
|
);
|
|
96
123
|
})
|
|
97
124
|
) : (
|
|
@@ -100,6 +127,7 @@ export function GcsaTransfer({
|
|
|
100
127
|
</div>
|
|
101
128
|
</section>
|
|
102
129
|
);
|
|
130
|
+
|
|
103
131
|
return (
|
|
104
132
|
<div className={`gcsa-transfer${disabled ? ' gcsa-transfer--disabled' : ''}`}>
|
|
105
133
|
{panel(availableItems, resolvedSourceTitle, 'source')}
|
package/src/GcsaUpload.tsx
CHANGED
|
@@ -152,7 +152,7 @@ export function GcsaUpload({
|
|
|
152
152
|
onDrop={onDrop}
|
|
153
153
|
>
|
|
154
154
|
<span className="gcsa-upload__icon" aria-hidden="true">
|
|
155
|
-
<GcsaIcon name="
|
|
155
|
+
<GcsaIcon name="upload" />
|
|
156
156
|
</span>
|
|
157
157
|
<span className="gcsa-upload__title">{resolvedButtonLabel}</span>
|
|
158
158
|
{resolvedHint ? <p className="gcsa-upload__hint">{resolvedHint}</p> : null}
|
package/src/icons/iconNames.ts
CHANGED
package/src/icons/registry.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GCSA_ICON_NAMES, type GcsaIconName } from './iconNames';
|
|
2
2
|
import chevronLeftSvg from './svg/chevron-left.svg?raw';
|
|
3
3
|
import menuSvg from './svg/menu.svg?raw';
|
|
4
|
+
import uploadSvg from './svg/upload.svg?raw';
|
|
4
5
|
|
|
5
6
|
const svgModules = import.meta.glob('./svg/*.svg', {
|
|
6
7
|
query: '?raw',
|
|
@@ -22,7 +23,8 @@ const registry = {
|
|
|
22
23
|
Object.entries(svgModules).map(([path, svg]) => [toIconKey(fileStem(path)), svg])
|
|
23
24
|
),
|
|
24
25
|
menu: menuSvg,
|
|
25
|
-
chevronLeft: chevronLeftSvg
|
|
26
|
+
chevronLeft: chevronLeftSvg,
|
|
27
|
+
upload: uploadSvg
|
|
26
28
|
} as Record<GcsaIconName, string>;
|
|
27
29
|
|
|
28
30
|
for (const name of GCSA_ICON_NAMES) {
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
<svg viewBox="0 0
|
|
1
|
+
<svg width="66" height="66" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<ellipse cx="33" cy="53" rx="33" ry="8" fill="#00BBCC" fill-opacity="0.1"/>
|
|
3
|
+
<path d="M35 19.5L12 26V49L35 47.5V19.5Z" fill="#00BBCC" fill-opacity="0.3"/>
|
|
4
|
+
<path d="M55 46.5V25L35 19.5V42L55 46.5Z" fill="#00BBCC" fill-opacity="0.4"/>
|
|
5
|
+
<path d="M54.4179 7.58906L57 9.00955L54.4106 10.4173L52.9909 13L51.5821 10.4109L49 8.99045L51.5894 7.5827L53.0091 5L54.4179 7.58906Z" fill="#00BBCC" fill-opacity="0.4"/>
|
|
6
|
+
<path d="M32 56.0806V32L55 25V48L32 56.0806Z" fill="#7CDBE4"/>
|
|
7
|
+
<path d="M32 56.0806V32L12 26V49L32 56.0806Z" fill="#97E3EA"/>
|
|
8
|
+
<path d="M25 7.5L35 19.5L12 26L2 14L25 7.5Z" fill="#CCF1F5"/>
|
|
9
|
+
<circle cx="35" cy="17.5" r="9" fill="#00BBCC"/>
|
|
10
|
+
<path d="M34.4 19.875V18.625H35C36.02 18.625 36.8 17.8125 36.8 16.75V16.125C36.8 15.0625 36.02 14.25 35 14.25C33.98 14.25 33.2 15.0625 33.2 16.125V16.75H32V16.125C32 14.375 33.32 13 35 13C36.68 13 38 14.375 38 16.125V16.75C38 18.25 36.98 19.5 35.6 19.8125V21.125H34.4V19.875ZM34.4 21.75H35.6V23H34.4V21.75Z" fill="white"/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
2
|
+
<path d="M32.5 20.2C31.4 15.7 27.5 12.5 22.8 12.5C18.6 12.5 15 15 13.5 18.6C10 19.1 7.5 22.1 7.5 25.7C7.5 29.6 10.7 32.8 14.6 32.8H32.2C35.6 32.8 38.5 30 38.5 26.5C38.5 23.3 35.9 20.7 32.5 20.2Z" stroke="currentColor" stroke-width="1.75" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M24 22.5V36" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/>
|
|
4
|
+
<path d="M19.5 27L24 22.5L28.5 27" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
+
</svg>
|