@via-profit/ui-kit 0.7.0 → 0.9.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/Autocomplete/AutocompleteContainer.d.ts +13 -0
- package/Autocomplete/AutocompleteContainer.js +46 -7
- package/Badge/BadgeBase.d.ts +10 -1
- package/Badge/BadgeBase.js +9 -2
- package/Badge/BadgeDot.d.ts +4 -0
- package/Badge/BadgeDot.js +20 -0
- package/Badge/BadgeStyled.js +67 -26
- package/Badge/index.d.ts +4 -4
- package/CHANGELOG.md +20 -0
- package/EmptyState/EmptyStateAction.d.ts +4 -0
- package/EmptyState/EmptyStateAction.js +16 -0
- package/EmptyState/EmptyStateContainer.d.ts +8 -0
- package/EmptyState/EmptyStateContainer.js +34 -0
- package/EmptyState/EmptyStateContent.d.ts +4 -0
- package/EmptyState/EmptyStateContent.js +15 -0
- package/EmptyState/EmptyStateIcon.d.ts +4 -0
- package/EmptyState/EmptyStateIcon.js +38 -0
- package/EmptyState/EmptyStateTitle.d.ts +4 -0
- package/EmptyState/EmptyStateTitle.js +13 -0
- package/EmptyState/index.d.ts +82 -0
- package/EmptyState/index.js +61 -0
- package/FileDropzone/FileDropzoneArea.d.ts +24 -0
- package/FileDropzone/FileDropzoneArea.js +81 -0
- package/FileDropzone/index.d.ts +107 -0
- package/FileDropzone/index.js +169 -0
- package/FileDropzone/utils.d.ts +9 -0
- package/FileDropzone/utils.js +42 -0
- package/HintPopover/HintPopoverButton.d.ts +4 -0
- package/HintPopover/HintPopoverButton.js +67 -0
- package/HintPopover/HintPopoverContainer.d.ts +4 -0
- package/HintPopover/HintPopoverContainer.js +52 -0
- package/HintPopover/index.d.ts +67 -0
- package/HintPopover/index.js +71 -0
- package/InfoNote/InfoNoteContainer.d.ts +14 -0
- package/InfoNote/InfoNoteContainer.js +56 -0
- package/InfoNote/InfoNoteContent.d.ts +4 -0
- package/InfoNote/InfoNoteContent.js +16 -0
- package/InfoNote/InfoNoteIcon.d.ts +9 -0
- package/InfoNote/InfoNoteIcon.js +47 -0
- package/InfoNote/InfoNoteTitle.d.ts +4 -0
- package/InfoNote/InfoNoteTitle.js +13 -0
- package/InfoNote/index.d.ts +65 -0
- package/InfoNote/index.js +59 -0
- package/Modal/Drawer/DrawerInner.d.ts +6 -0
- package/Modal/Drawer/DrawerInner.js +91 -2
- package/Modal/Drawer/index.d.ts +8 -0
- package/Modal/Drawer/index.js +3 -3
- package/Modal/Drawer/useSwipeToClose.d.ts +30 -0
- package/Modal/Drawer/useSwipeToClose.js +238 -0
- package/Skeleton/index.d.ts +39 -0
- package/Skeleton/index.js +69 -0
- package/esm/Autocomplete/AutocompleteContainer.js +46 -7
- package/esm/Badge/BadgeBase.js +9 -2
- package/esm/Badge/BadgeDot.js +15 -0
- package/esm/Badge/BadgeStyled.js +67 -26
- package/esm/EmptyState/EmptyStateAction.js +11 -0
- package/esm/EmptyState/EmptyStateContainer.js +29 -0
- package/esm/EmptyState/EmptyStateContent.js +10 -0
- package/esm/EmptyState/EmptyStateIcon.js +33 -0
- package/esm/EmptyState/EmptyStateTitle.js +8 -0
- package/esm/EmptyState/index.js +42 -0
- package/esm/FileDropzone/FileDropzoneArea.js +76 -0
- package/esm/FileDropzone/index.js +148 -0
- package/esm/FileDropzone/utils.js +37 -0
- package/esm/HintPopover/HintPopoverButton.js +62 -0
- package/esm/HintPopover/HintPopoverContainer.js +47 -0
- package/esm/HintPopover/index.js +52 -0
- package/esm/InfoNote/InfoNoteContainer.js +49 -0
- package/esm/InfoNote/InfoNoteContent.js +11 -0
- package/esm/InfoNote/InfoNoteIcon.js +42 -0
- package/esm/InfoNote/InfoNoteTitle.js +8 -0
- package/esm/InfoNote/index.js +40 -0
- package/esm/Modal/Drawer/DrawerInner.js +91 -2
- package/esm/Modal/Drawer/index.js +3 -3
- package/esm/Modal/Drawer/useSwipeToClose.js +233 -0
- package/esm/Skeleton/index.js +64 -0
- package/package.json +126 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnchorVariant } from './DrawerInner';
|
|
3
|
+
export type UseSwipeToCloseOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* The drawer element: it follows the finger
|
|
6
|
+
*/
|
|
7
|
+
readonly elementRef: React.RefObject<HTMLDivElement | null>;
|
|
8
|
+
readonly enabled: boolean;
|
|
9
|
+
readonly isOpen: boolean;
|
|
10
|
+
readonly anchor: AnchorVariant;
|
|
11
|
+
readonly onClose: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Closes the drawer by a swipe towards its edge.
|
|
15
|
+
*
|
|
16
|
+
* The pointer events move the drawer. A touch would be taken by the browser for the scroll
|
|
17
|
+
* (and the pointer cancelled), so the native `touchmove` is prevented while the drawer is dragged
|
|
18
|
+
*/
|
|
19
|
+
declare const useSwipeToClose: (options: UseSwipeToCloseOptions) => {
|
|
20
|
+
onPointerDown?: undefined;
|
|
21
|
+
onPointerMove?: undefined;
|
|
22
|
+
onPointerUp?: undefined;
|
|
23
|
+
onPointerCancel?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
26
|
+
onPointerMove: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
27
|
+
onPointerUp: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
28
|
+
onPointerCancel: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
29
|
+
};
|
|
30
|
+
export default useSwipeToClose;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
// The finger moves this far before the gesture is recognized as a drag or a scroll
|
|
8
|
+
const SLOP = 6;
|
|
9
|
+
// The drawer is closed when dragged by this part of its size or thrown faster than this
|
|
10
|
+
const CLOSE_RATIO = 0.25;
|
|
11
|
+
const CLOSE_VELOCITY = 0.5; // px per ms
|
|
12
|
+
const RETURN_TRANSITION = 'transform 240ms ease-out';
|
|
13
|
+
const initialState = {
|
|
14
|
+
mode: 'idle',
|
|
15
|
+
pointerId: -1,
|
|
16
|
+
startX: 0,
|
|
17
|
+
startY: 0,
|
|
18
|
+
offset: 0,
|
|
19
|
+
lastOffset: 0,
|
|
20
|
+
lastTime: 0,
|
|
21
|
+
velocity: 0,
|
|
22
|
+
};
|
|
23
|
+
const isVertical = (anchor) => anchor === 'bottom' || anchor === 'top';
|
|
24
|
+
/**
|
|
25
|
+
* The movement of the finger towards the edge of the drawer: positive closes it
|
|
26
|
+
*/
|
|
27
|
+
const getClosingDelta = (anchor, dx, dy) => {
|
|
28
|
+
switch (anchor) {
|
|
29
|
+
case 'top':
|
|
30
|
+
return -dy;
|
|
31
|
+
case 'left':
|
|
32
|
+
return -dx;
|
|
33
|
+
case 'right':
|
|
34
|
+
return dx;
|
|
35
|
+
case 'bottom':
|
|
36
|
+
default:
|
|
37
|
+
return dy;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const getTransform = (anchor, offset) => {
|
|
41
|
+
switch (anchor) {
|
|
42
|
+
case 'top':
|
|
43
|
+
return `translateY(${-offset}px)`;
|
|
44
|
+
case 'left':
|
|
45
|
+
return `translateX(${-offset}px)`;
|
|
46
|
+
case 'right':
|
|
47
|
+
return `translateX(${offset}px)`;
|
|
48
|
+
case 'bottom':
|
|
49
|
+
default:
|
|
50
|
+
return `translateY(${offset}px)`;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Whether an element between the target and the drawer can scroll the way the finger moves.
|
|
55
|
+
* Then the finger scrolls the content: the drawer is dragged only when the content is at its edge
|
|
56
|
+
*/
|
|
57
|
+
const canScrollWithGesture = (target, root, anchor) => {
|
|
58
|
+
let element = target instanceof Element ? target : null;
|
|
59
|
+
while (element && element !== root) {
|
|
60
|
+
if (element instanceof HTMLElement) {
|
|
61
|
+
const style = window.getComputedStyle(element);
|
|
62
|
+
const overflow = isVertical(anchor) ? style.overflowY : style.overflowX;
|
|
63
|
+
const scrollable = overflow === 'auto' || overflow === 'scroll';
|
|
64
|
+
if (scrollable) {
|
|
65
|
+
const { scrollTop, scrollLeft, scrollHeight, scrollWidth, clientHeight, clientWidth } = element;
|
|
66
|
+
switch (anchor) {
|
|
67
|
+
case 'bottom':
|
|
68
|
+
if (scrollTop > 0) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case 'top':
|
|
73
|
+
if (scrollTop + clientHeight < scrollHeight - 1) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case 'left':
|
|
78
|
+
if (scrollLeft + clientWidth < scrollWidth - 1) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case 'right':
|
|
83
|
+
if (scrollLeft > 0) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
element = element.parentElement;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Closes the drawer by a swipe towards its edge.
|
|
98
|
+
*
|
|
99
|
+
* The pointer events move the drawer. A touch would be taken by the browser for the scroll
|
|
100
|
+
* (and the pointer cancelled), so the native `touchmove` is prevented while the drawer is dragged
|
|
101
|
+
*/
|
|
102
|
+
const useSwipeToClose = (options) => {
|
|
103
|
+
const { elementRef, enabled, isOpen, anchor, onClose } = options;
|
|
104
|
+
const stateRef = react_1.default.useRef(Object.assign({}, initialState));
|
|
105
|
+
const setOffset = react_1.default.useCallback((offset, transition) => {
|
|
106
|
+
const element = elementRef.current;
|
|
107
|
+
if (element) {
|
|
108
|
+
element.style.transition = transition;
|
|
109
|
+
element.style.transform = offset > 0 ? getTransform(anchor, offset) : '';
|
|
110
|
+
}
|
|
111
|
+
}, [anchor, elementRef]);
|
|
112
|
+
const restore = react_1.default.useCallback(() => {
|
|
113
|
+
const element = elementRef.current;
|
|
114
|
+
if (element) {
|
|
115
|
+
element.style.transition = '';
|
|
116
|
+
element.style.transform = '';
|
|
117
|
+
}
|
|
118
|
+
}, [elementRef]);
|
|
119
|
+
// A drawer closed by a swipe keeps its offset while it slides out: reset it for the next opening
|
|
120
|
+
react_1.default.useEffect(() => {
|
|
121
|
+
if (isOpen) {
|
|
122
|
+
stateRef.current = Object.assign({}, initialState);
|
|
123
|
+
restore();
|
|
124
|
+
}
|
|
125
|
+
}, [isOpen, restore]);
|
|
126
|
+
/**
|
|
127
|
+
* Decides on the first noticeable movement whether it drags the drawer or scrolls the content
|
|
128
|
+
*/
|
|
129
|
+
const update = react_1.default.useCallback((clientX, clientY, target) => {
|
|
130
|
+
const state = stateRef.current;
|
|
131
|
+
const element = elementRef.current;
|
|
132
|
+
if (!element || (state.mode !== 'pending' && state.mode !== 'drag')) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const dx = clientX - state.startX;
|
|
136
|
+
const dy = clientY - state.startY;
|
|
137
|
+
const closing = getClosingDelta(anchor, dx, dy);
|
|
138
|
+
if (state.mode === 'pending') {
|
|
139
|
+
const main = isVertical(anchor) ? Math.abs(dy) : Math.abs(dx);
|
|
140
|
+
const cross = isVertical(anchor) ? Math.abs(dx) : Math.abs(dy);
|
|
141
|
+
if (main < SLOP && cross < SLOP) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const isDrag = closing > 0 && main >= cross && !canScrollWithGesture(target, element, anchor);
|
|
145
|
+
state.mode = isDrag ? 'drag' : 'scroll';
|
|
146
|
+
if (!isDrag) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// The drag starts from here, without a jump by the slop
|
|
150
|
+
state.startX = clientX;
|
|
151
|
+
state.startY = clientY;
|
|
152
|
+
state.lastTime = performance.now();
|
|
153
|
+
}
|
|
154
|
+
const offset = Math.max(0, getClosingDelta(anchor, clientX - state.startX, clientY - state.startY));
|
|
155
|
+
const now = performance.now();
|
|
156
|
+
const elapsed = now - state.lastTime;
|
|
157
|
+
if (elapsed > 0) {
|
|
158
|
+
state.velocity = (offset - state.lastOffset) / elapsed;
|
|
159
|
+
}
|
|
160
|
+
state.lastOffset = offset;
|
|
161
|
+
state.lastTime = now;
|
|
162
|
+
state.offset = offset;
|
|
163
|
+
setOffset(offset, 'none');
|
|
164
|
+
}, [anchor, elementRef, setOffset]);
|
|
165
|
+
// A non-passive listener: only it may stop the browser from scrolling under the drag
|
|
166
|
+
react_1.default.useEffect(() => {
|
|
167
|
+
const element = elementRef.current;
|
|
168
|
+
if (!enabled || !element) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
const handleTouchMove = (event) => {
|
|
172
|
+
const touch = event.touches[0];
|
|
173
|
+
if (touch) {
|
|
174
|
+
update(touch.clientX, touch.clientY, event.target);
|
|
175
|
+
}
|
|
176
|
+
if (stateRef.current.mode === 'drag' && event.cancelable) {
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
element.addEventListener('touchmove', handleTouchMove, { passive: false });
|
|
181
|
+
return () => {
|
|
182
|
+
element.removeEventListener('touchmove', handleTouchMove);
|
|
183
|
+
};
|
|
184
|
+
}, [enabled, elementRef, update]);
|
|
185
|
+
const onPointerDown = react_1.default.useCallback((event) => {
|
|
186
|
+
if (!event.isPrimary || event.button !== 0) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// A mouse drags only by the handle: everywhere else it selects the text
|
|
190
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
191
|
+
if (event.pointerType === 'mouse') {
|
|
192
|
+
if (!(target === null || target === void 0 ? void 0 : target.closest('[data-drawer-handle]'))) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
197
|
+
}
|
|
198
|
+
stateRef.current = Object.assign(Object.assign({}, initialState), { mode: 'pending', pointerId: event.pointerId, startX: event.clientX, startY: event.clientY, lastTime: performance.now() });
|
|
199
|
+
}, []);
|
|
200
|
+
const onPointerMove = react_1.default.useCallback((event) => {
|
|
201
|
+
if (event.pointerId === stateRef.current.pointerId) {
|
|
202
|
+
update(event.clientX, event.clientY, event.target);
|
|
203
|
+
}
|
|
204
|
+
}, [update]);
|
|
205
|
+
const finish = react_1.default.useCallback((event, cancelled) => {
|
|
206
|
+
const state = stateRef.current;
|
|
207
|
+
if (event.pointerId !== state.pointerId) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const wasDragged = state.mode === 'drag';
|
|
211
|
+
const element = elementRef.current;
|
|
212
|
+
const size = element ? (isVertical(anchor) ? element.offsetHeight : element.offsetWidth) : 0;
|
|
213
|
+
const shouldClose = wasDragged &&
|
|
214
|
+
!cancelled &&
|
|
215
|
+
(state.offset > size * CLOSE_RATIO || state.velocity > CLOSE_VELOCITY);
|
|
216
|
+
stateRef.current = Object.assign({}, initialState);
|
|
217
|
+
if (!wasDragged) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (shouldClose) {
|
|
221
|
+
// The offset is kept: the drawer slides out from where the finger left it.
|
|
222
|
+
// The own transitions of the drawer are back, the drag turned them off
|
|
223
|
+
if (element) {
|
|
224
|
+
element.style.transition = '';
|
|
225
|
+
}
|
|
226
|
+
onClose(event);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
setOffset(0, RETURN_TRANSITION);
|
|
230
|
+
}, [anchor, elementRef, onClose, setOffset]);
|
|
231
|
+
const onPointerUp = react_1.default.useCallback((event) => finish(event, false), [finish]);
|
|
232
|
+
const onPointerCancel = react_1.default.useCallback((event) => finish(event, true), [finish]);
|
|
233
|
+
if (!enabled) {
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
236
|
+
return { onPointerDown, onPointerMove, onPointerUp, onPointerCancel };
|
|
237
|
+
};
|
|
238
|
+
exports.default = useSwipeToClose;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type SkeletonVariant = 'text' | 'rect' | 'circle' | 'pill';
|
|
3
|
+
export type SkeletonProps = React.HTMLAttributes<HTMLSpanElement> & {
|
|
4
|
+
/**
|
|
5
|
+
* `text` — the line of the text, `rect` — the block (an image, a card), `circle` — an avatar,
|
|
6
|
+
* `pill` — a badge or a status\
|
|
7
|
+
* **Default:** `text`
|
|
8
|
+
*/
|
|
9
|
+
readonly variant?: SkeletonVariant;
|
|
10
|
+
/**
|
|
11
|
+
* The CSS width; a number is in `em`\
|
|
12
|
+
* **Default:** `100%`
|
|
13
|
+
*/
|
|
14
|
+
readonly width?: number | string;
|
|
15
|
+
/**
|
|
16
|
+
* The CSS height; a number is in `em`\
|
|
17
|
+
* **Default:** by the variant
|
|
18
|
+
*/
|
|
19
|
+
readonly height?: number | string;
|
|
20
|
+
};
|
|
21
|
+
declare const _default: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & {
|
|
22
|
+
/**
|
|
23
|
+
* `text` — the line of the text, `rect` — the block (an image, a card), `circle` — an avatar,
|
|
24
|
+
* `pill` — a badge or a status\
|
|
25
|
+
* **Default:** `text`
|
|
26
|
+
*/
|
|
27
|
+
readonly variant?: SkeletonVariant;
|
|
28
|
+
/**
|
|
29
|
+
* The CSS width; a number is in `em`\
|
|
30
|
+
* **Default:** `100%`
|
|
31
|
+
*/
|
|
32
|
+
readonly width?: number | string;
|
|
33
|
+
/**
|
|
34
|
+
* The CSS height; a number is in `em`\
|
|
35
|
+
* **Default:** by the variant
|
|
36
|
+
*/
|
|
37
|
+
readonly height?: number | string;
|
|
38
|
+
} & React.RefAttributes<HTMLSpanElement>>;
|
|
39
|
+
export default _default;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
const react_1 = __importDefault(require("react"));
|
|
18
|
+
const styled_1 = __importDefault(require("@emotion/styled"));
|
|
19
|
+
const react_2 = require("@emotion/react");
|
|
20
|
+
const size = (value) => (typeof value === 'number' ? `${value}em` : value);
|
|
21
|
+
const DEFAULT_HEIGHT = {
|
|
22
|
+
text: '0.8em',
|
|
23
|
+
rect: '3em',
|
|
24
|
+
circle: '2.5em',
|
|
25
|
+
pill: '1.6em',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The solid shape fades in and out: calmer than the moving gradient and does not depend on the width
|
|
29
|
+
*/
|
|
30
|
+
const pulse = (0, react_2.keyframes) `
|
|
31
|
+
0%,
|
|
32
|
+
100% {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
}
|
|
35
|
+
50% {
|
|
36
|
+
opacity: 0.4;
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
const StyledSkeleton = styled_1.default.span `
|
|
40
|
+
display: block;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
width: ${({ $width }) => $width};
|
|
43
|
+
max-width: 100%;
|
|
44
|
+
height: ${({ $height }) => $height};
|
|
45
|
+
border-radius: ${({ theme, $variant }) => $variant === 'circle' || $variant === 'pill'
|
|
46
|
+
? '999em'
|
|
47
|
+
: `${theme.shape.radiusFactor * ($variant === 'rect' ? 1.5 : 0.75)}em`};
|
|
48
|
+
background-color: ${({ theme }) => theme.color.textPrimary.alpha(0.08).toString()};
|
|
49
|
+
animation: ${pulse} 1.6s ease-in-out infinite;
|
|
50
|
+
|
|
51
|
+
${({ $variant, $height }) => $variant === 'circle' &&
|
|
52
|
+
(0, react_2.css) `
|
|
53
|
+
aspect-ratio: 1;
|
|
54
|
+
width: ${$height};
|
|
55
|
+
`}
|
|
56
|
+
|
|
57
|
+
@media (prefers-reduced-motion: reduce) {
|
|
58
|
+
animation: none;
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
/**
|
|
62
|
+
* The placeholder of the content which is loaded: the gray shape which fades in and out.
|
|
63
|
+
* Shows the structure of the content instead of the spinner
|
|
64
|
+
*/
|
|
65
|
+
const Skeleton = (props, ref) => {
|
|
66
|
+
const { variant = 'text', width = '100%', height } = props, nativeProps = __rest(props, ["variant", "width", "height"]);
|
|
67
|
+
return (react_1.default.createElement(StyledSkeleton, Object.assign({ "aria-hidden": true }, nativeProps, { ref: ref, "$variant": variant, "$width": size(width), "$height": height !== undefined ? size(height) : DEFAULT_HEIGHT[variant] })));
|
|
68
|
+
};
|
|
69
|
+
exports.default = react_1.default.forwardRef(Skeleton);
|
|
@@ -29,7 +29,7 @@ const AutocompleteMenuList = React.forwardRef((props, ref) => (React.createEleme
|
|
|
29
29
|
AutocompleteMenuList.displayName = 'AutocompleteMenuList';
|
|
30
30
|
const MENU_OVERRIDES = { List: AutocompleteMenuList };
|
|
31
31
|
const Autocomplete = React.forwardRef((props, ref) => {
|
|
32
|
-
const { items, value, multiple, isOpen = false, isLoading = false, clearOnBlur = true, anchorPos = 'bottom-fill', alternativePlacements = ['bottom-fill', 'top-fill'], openOnFocus = true, autoFlip = true, viewportMargin, positionStrategy, requiredAsterisk, startIcon, fullWidth, placeholder, label, error, errorText, inputRef, filterItems, children, onChange, selectedItemToString, clearable = true, onInputChange, getOptionSelected, onRequestOpen = () => undefined, onRequestClose = () => undefined, overrides } = props, nativeInputProps = __rest(props, ["items", "value", "multiple", "isOpen", "isLoading", "clearOnBlur", "anchorPos", "alternativePlacements", "openOnFocus", "autoFlip", "viewportMargin", "positionStrategy", "requiredAsterisk", "startIcon", "fullWidth", "placeholder", "label", "error", "errorText", "inputRef", "filterItems", "children", "onChange", "selectedItemToString", "clearable", "onInputChange", "getOptionSelected", "onRequestOpen", "onRequestClose", "overrides"]);
|
|
32
|
+
const { items, value, multiple, isOpen = false, isLoading = false, clearOnBlur = true, anchorPos = 'bottom-fill', alternativePlacements = ['bottom-fill', 'top-fill'], openOnFocus = true, autoFlip = true, viewportMargin, positionStrategy, requiredAsterisk, startIcon, fullWidth, placeholder, label, error, errorText, inputRef, filterItems, children, onChange, selectedItemToString, clearable = true, onInputChange, inputValue: controlledInputValue, onInputValueChange, getOptionSelected, onRequestOpen = () => undefined, onRequestClose = () => undefined, overrides } = props, nativeInputProps = __rest(props, ["items", "value", "multiple", "isOpen", "isLoading", "clearOnBlur", "anchorPos", "alternativePlacements", "openOnFocus", "autoFlip", "viewportMargin", "positionStrategy", "requiredAsterisk", "startIcon", "fullWidth", "placeholder", "label", "error", "errorText", "inputRef", "filterItems", "children", "onChange", "selectedItemToString", "clearable", "onInputChange", "inputValue", "onInputValueChange", "getOptionSelected", "onRequestOpen", "onRequestClose", "overrides"]);
|
|
33
33
|
const menuRef = React.useRef(null);
|
|
34
34
|
const fieldInputRef = React.useRef(null);
|
|
35
35
|
const isFocusedRef = React.useRef(false);
|
|
@@ -39,10 +39,12 @@ const Autocomplete = React.forwardRef((props, ref) => {
|
|
|
39
39
|
const overridesMap = React.useMemo(() => ({
|
|
40
40
|
TextField: (overrides === null || overrides === void 0 ? void 0 : overrides.TextField) || TextField,
|
|
41
41
|
}), [overrides]);
|
|
42
|
+
const isInputControlled = typeof controlledInputValue === 'string';
|
|
42
43
|
const clear = React.useCallback(() => {
|
|
43
44
|
if (onChange) {
|
|
44
45
|
onChange((multiple ? [] : null));
|
|
45
46
|
}
|
|
47
|
+
onInputValueChange === null || onInputValueChange === void 0 ? void 0 : onInputValueChange('');
|
|
46
48
|
dispatch(actionSetPartial({
|
|
47
49
|
filteredItems: items,
|
|
48
50
|
inputValue: '',
|
|
@@ -51,7 +53,7 @@ const Autocomplete = React.forwardRef((props, ref) => {
|
|
|
51
53
|
var _a;
|
|
52
54
|
(_a = fieldInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
53
55
|
}, 15);
|
|
54
|
-
}, [dispatch, onChange, items, multiple]);
|
|
56
|
+
}, [dispatch, onChange, onInputValueChange, items, multiple]);
|
|
55
57
|
/**
|
|
56
58
|
* API
|
|
57
59
|
*/
|
|
@@ -70,13 +72,27 @@ const Autocomplete = React.forwardRef((props, ref) => {
|
|
|
70
72
|
*/
|
|
71
73
|
const restoreInputValue = React.useCallback(() => {
|
|
72
74
|
// With multiple the field holds only the search text, the selected items are tags
|
|
73
|
-
|
|
75
|
+
// The controlled text is the value itself: it stays as it is
|
|
76
|
+
const newInputValue = isInputControlled
|
|
77
|
+
? controlledInputValue
|
|
78
|
+
: multiple || currentValue === null
|
|
79
|
+
? ''
|
|
80
|
+
: selectedItemToString(currentValue);
|
|
74
81
|
const newFilteredItems = applyFilterForItems(newInputValue, items);
|
|
75
82
|
dispatch(actionSetPartial({
|
|
76
83
|
filteredItems: newFilteredItems,
|
|
77
84
|
inputValue: newInputValue,
|
|
78
85
|
}));
|
|
79
|
-
}, [
|
|
86
|
+
}, [
|
|
87
|
+
currentValue,
|
|
88
|
+
selectedItemToString,
|
|
89
|
+
multiple,
|
|
90
|
+
applyFilterForItems,
|
|
91
|
+
items,
|
|
92
|
+
dispatch,
|
|
93
|
+
isInputControlled,
|
|
94
|
+
controlledInputValue,
|
|
95
|
+
]);
|
|
80
96
|
const inputKeydownEvent = React.useCallback((event) => {
|
|
81
97
|
var _a, _b, _c, _d, _e;
|
|
82
98
|
if (!isFocusedRef.current) {
|
|
@@ -169,14 +185,35 @@ const Autocomplete = React.forwardRef((props, ref) => {
|
|
|
169
185
|
if (value === currentValue) {
|
|
170
186
|
return;
|
|
171
187
|
}
|
|
172
|
-
// With multiple the selected items are shown as tags and the search text is kept
|
|
173
|
-
|
|
188
|
+
// With multiple the selected items are shown as tags and the search text is kept,
|
|
189
|
+
// the controlled text is not replaced by the text of the item
|
|
190
|
+
dispatch(actionSetPartial(multiple || isInputControlled
|
|
174
191
|
? { currentValue: value }
|
|
175
192
|
: {
|
|
176
193
|
currentValue: value,
|
|
177
194
|
inputValue: value === null ? '' : selectedItemToString(value),
|
|
178
195
|
}));
|
|
179
|
-
}, [
|
|
196
|
+
}, [
|
|
197
|
+
value,
|
|
198
|
+
multiple,
|
|
199
|
+
currentValue,
|
|
200
|
+
inputValue,
|
|
201
|
+
dispatch,
|
|
202
|
+
selectedItemToString,
|
|
203
|
+
isInputControlled,
|
|
204
|
+
]);
|
|
205
|
+
/**
|
|
206
|
+
* The controlled text: the field follows `inputValue`
|
|
207
|
+
*/
|
|
208
|
+
React.useEffect(() => {
|
|
209
|
+
if (!isInputControlled || controlledInputValue === inputValue) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
dispatch(actionSetPartial({
|
|
213
|
+
inputValue: controlledInputValue,
|
|
214
|
+
filteredItems: applyFilterForItems(controlledInputValue, items),
|
|
215
|
+
}));
|
|
216
|
+
}, [isInputControlled, controlledInputValue, inputValue, dispatch, applyFilterForItems, items]);
|
|
180
217
|
React.useEffect(() => {
|
|
181
218
|
if (currentOpen && menuRef.current) {
|
|
182
219
|
menuRef.current.scrollToFirstSelected();
|
|
@@ -267,6 +304,7 @@ const Autocomplete = React.forwardRef((props, ref) => {
|
|
|
267
304
|
if (typeof onInputChange === 'function') {
|
|
268
305
|
onInputChange(event);
|
|
269
306
|
}
|
|
307
|
+
onInputValueChange === null || onInputValueChange === void 0 ? void 0 : onInputValueChange(event.currentTarget.value);
|
|
270
308
|
// If openOnFocus is false, but value is not empty
|
|
271
309
|
// we should open menu list if is not opened
|
|
272
310
|
if (!openOnFocus && isFocusedRef.current && !currentOpen && filteredItems.length > 0) {
|
|
@@ -290,6 +328,7 @@ const Autocomplete = React.forwardRef((props, ref) => {
|
|
|
290
328
|
filteredItems.length,
|
|
291
329
|
items,
|
|
292
330
|
onInputChange,
|
|
331
|
+
onInputValueChange,
|
|
293
332
|
onRequestClose,
|
|
294
333
|
onRequestOpen,
|
|
295
334
|
openOnFocus,
|
package/esm/Badge/BadgeBase.js
CHANGED
|
@@ -14,14 +14,16 @@ import IconWrapper from './BadgeIconWrapper';
|
|
|
14
14
|
import TextWrapper from './BadgeTextWrapper';
|
|
15
15
|
import Container from './BadgeContainer';
|
|
16
16
|
import ButtonDelete from './BadgeDeleteButton';
|
|
17
|
+
import Dot from './BadgeDot';
|
|
17
18
|
const BadgeBase = (props, ref) => {
|
|
18
|
-
const { children, startIcon, color, variant, overrides, onDelete, deleteButtonLabel = 'Delete', onClick, onKeyDown } = props, nativeProps = __rest(props, ["children", "startIcon", "color", "variant", "overrides", "onDelete", "deleteButtonLabel", "onClick", "onKeyDown"]);
|
|
19
|
+
const { children, startIcon, dot = false, color, variant, overrides, onDelete, deleteButtonLabel = 'Delete', onClick, onKeyDown } = props, nativeProps = __rest(props, ["children", "startIcon", "dot", "color", "variant", "overrides", "onDelete", "deleteButtonLabel", "onClick", "onKeyDown"]);
|
|
19
20
|
const clickable = typeof onClick === 'function';
|
|
20
21
|
const overridesMap = React.useMemo(() => ({
|
|
21
22
|
TextWrapper: (overrides === null || overrides === void 0 ? void 0 : overrides.TextWrapper) || TextWrapper,
|
|
22
23
|
IconWrapper: (overrides === null || overrides === void 0 ? void 0 : overrides.IconWrapper) || IconWrapper,
|
|
23
24
|
Container: (overrides === null || overrides === void 0 ? void 0 : overrides.Container) || Container,
|
|
24
25
|
ButtonDelete: (overrides === null || overrides === void 0 ? void 0 : overrides.ButtonDelete) || ButtonDelete,
|
|
26
|
+
Dot: (overrides === null || overrides === void 0 ? void 0 : overrides.Dot) || Dot,
|
|
25
27
|
}), [overrides]);
|
|
26
28
|
// A clickable badge acts as a button, so it is activated by Enter and Space too
|
|
27
29
|
const handleKeyDown = React.useCallback((event) => {
|
|
@@ -41,8 +43,13 @@ const BadgeBase = (props, ref) => {
|
|
|
41
43
|
onDelete === null || onDelete === void 0 ? void 0 : onDelete(event);
|
|
42
44
|
}, [onDelete]);
|
|
43
45
|
return (React.createElement(overridesMap.Container, Object.assign({ role: clickable ? 'button' : undefined, tabIndex: clickable ? 0 : undefined }, nativeProps, { onClick: onClick, onKeyDown: handleKeyDown, color: color, ref: ref }),
|
|
46
|
+
dot && React.createElement(overridesMap.Dot, null),
|
|
44
47
|
typeof startIcon !== 'undefined' && startIcon !== null && (React.createElement(overridesMap.IconWrapper, null, startIcon)),
|
|
45
48
|
React.createElement(overridesMap.TextWrapper, null, children),
|
|
46
|
-
typeof onDelete === 'function' && (React.createElement(overridesMap.ButtonDelete
|
|
49
|
+
typeof onDelete === 'function' && (React.createElement(overridesMap.ButtonDelete
|
|
50
|
+
// the pale soft badge has the colored icon, as the outlined one
|
|
51
|
+
, {
|
|
52
|
+
// the pale soft badge has the colored icon, as the outlined one
|
|
53
|
+
variant: variant === 'soft' ? 'outlined' : variant, onClick: handleDelete, color: color, "aria-label": deleteButtonLabel }))));
|
|
47
54
|
};
|
|
48
55
|
export default React.forwardRef(BadgeBase);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
/**
|
|
4
|
+
* The color of the badge (`--badge-dot-color`), not the color of its text
|
|
5
|
+
*/
|
|
6
|
+
const StyledDot = styled.span `
|
|
7
|
+
flex: none;
|
|
8
|
+
width: 0.5em;
|
|
9
|
+
height: 0.5em;
|
|
10
|
+
margin-left: 0.55em;
|
|
11
|
+
border-radius: 50%;
|
|
12
|
+
background-color: var(--badge-dot-color, currentColor);
|
|
13
|
+
`;
|
|
14
|
+
const BadgeDot = (props, ref) => (React.createElement(StyledDot, Object.assign({ "aria-hidden": true }, props, { ref: ref })));
|
|
15
|
+
export default React.forwardRef(BadgeDot);
|
package/esm/Badge/BadgeStyled.js
CHANGED
|
@@ -65,6 +65,23 @@ const getOutlinedColors = (color, theme) => {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* The pale background of the color and the text of the same hue: dark on the light theme,
|
|
70
|
+
* light on the dark one. `$background` is the color itself
|
|
71
|
+
*/
|
|
72
|
+
const getSoftColors = (color, theme) => {
|
|
73
|
+
const base = color === 'primary'
|
|
74
|
+
? theme.color.accentPrimary
|
|
75
|
+
: color === 'secondary'
|
|
76
|
+
? theme.color.accentSecondary
|
|
77
|
+
: typeof color === 'undefined' || color === 'default'
|
|
78
|
+
? theme.color.textPrimary
|
|
79
|
+
: parseColor(String(color), theme.color.textPrimary);
|
|
80
|
+
return {
|
|
81
|
+
$background: base,
|
|
82
|
+
$color: theme.isDark ? base.lighten(60) : base.darken(60),
|
|
83
|
+
};
|
|
84
|
+
};
|
|
68
85
|
/**
|
|
69
86
|
* Both variants are one styled component: switching the variant (e.g. a toggling filter badge)
|
|
70
87
|
* must not remount the element, otherwise the focused badge loses the focus
|
|
@@ -73,42 +90,62 @@ const StyledBadge = styled(BadgeBase) `
|
|
|
73
90
|
border-style: solid;
|
|
74
91
|
border-width: ${BORDER_WIDTH};
|
|
75
92
|
border-color: ${({ $background }) => $background.toString()};
|
|
93
|
+
--badge-dot-color: ${({ $variant, $background, $color }) => ($variant === 'standard' ? $color : $background).toString()};
|
|
76
94
|
|
|
77
|
-
${({ $variant, $color, $background
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
${({ $variant, $color, $background }) => $variant === 'soft' &&
|
|
96
|
+
css `
|
|
97
|
+
color: ${$color.toString()};
|
|
98
|
+
background-color: ${$background.alpha(0.18).toString()};
|
|
99
|
+
border-color: transparent;
|
|
81
100
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
: css `
|
|
89
|
-
color: ${$color.toString()};
|
|
90
|
-
background-color: ${$background.toString()};
|
|
101
|
+
&:focus-visible {
|
|
102
|
+
outline-style: solid;
|
|
103
|
+
outline-width: 0.14em;
|
|
104
|
+
outline-color: ${$background.alpha(0.6).toString()};
|
|
105
|
+
}
|
|
106
|
+
`}
|
|
91
107
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
${({ $variant, $color, $background, theme }) => $variant === 'soft'
|
|
109
|
+
? ''
|
|
110
|
+
: $variant === 'outlined'
|
|
111
|
+
? css `
|
|
112
|
+
color: ${$color.darken(30).toString()};
|
|
113
|
+
background-color: transparent;
|
|
114
|
+
|
|
115
|
+
&:focus-visible {
|
|
116
|
+
outline-style: solid;
|
|
117
|
+
outline-width: 0.14em;
|
|
118
|
+
outline-color: ${$background.rgbString()};
|
|
119
|
+
}
|
|
120
|
+
`
|
|
121
|
+
: css `
|
|
122
|
+
color: ${$color.toString()};
|
|
123
|
+
background-color: ${$background.toString()};
|
|
124
|
+
|
|
125
|
+
&:focus-visible {
|
|
126
|
+
outline-color: ${$background.rgbString() === theme.color.accentPrimary.rgbString()
|
|
127
|
+
? theme.color.textPrimary.toString()
|
|
128
|
+
: theme.color.accentPrimary.toString()};
|
|
129
|
+
}
|
|
130
|
+
`}
|
|
98
131
|
|
|
99
132
|
${({ $clickable, $variant, $background }) => $clickable &&
|
|
100
133
|
css `
|
|
101
134
|
cursor: pointer;
|
|
102
135
|
|
|
103
136
|
&:hover {
|
|
104
|
-
${$variant === '
|
|
137
|
+
${$variant === 'soft'
|
|
105
138
|
? css `
|
|
106
|
-
background-color: ${$background.
|
|
139
|
+
background-color: ${$background.alpha(0.3).toString()};
|
|
107
140
|
`
|
|
108
|
-
:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
141
|
+
: $variant === 'outlined'
|
|
142
|
+
? css `
|
|
143
|
+
background-color: ${$background.darken(20).alpha(0.1).toString()};
|
|
144
|
+
`
|
|
145
|
+
: css `
|
|
146
|
+
background-color: ${$background.darken(30).toString()};
|
|
147
|
+
border-color: ${$background.darken(30).toString()};
|
|
148
|
+
`}
|
|
112
149
|
}
|
|
113
150
|
`}
|
|
114
151
|
${({ theme }) => themeFocusRing(theme)}
|
|
@@ -116,7 +153,11 @@ const StyledBadge = styled(BadgeBase) `
|
|
|
116
153
|
const BadgeStyled = (props, ref) => {
|
|
117
154
|
const { children, color, variant = 'standard', onClick } = props, restProps = __rest(props, ["children", "color", "variant", "onClick"]);
|
|
118
155
|
const theme = useTheme();
|
|
119
|
-
const { $background, $color } = React.useMemo(() => variant === '
|
|
156
|
+
const { $background, $color } = React.useMemo(() => variant === 'soft'
|
|
157
|
+
? getSoftColors(color, theme)
|
|
158
|
+
: variant === 'outlined'
|
|
159
|
+
? getOutlinedColors(color, theme)
|
|
160
|
+
: getStandardColors(color, theme), [color, variant, theme]);
|
|
120
161
|
return (React.createElement(StyledBadge, Object.assign({ "$variant": variant, "$clickable": typeof onClick === 'function', "$color": $color, "$background": $background, color: color, variant: variant, onClick: onClick }, restProps, { ref: ref }), children));
|
|
121
162
|
};
|
|
122
163
|
export default React.forwardRef(BadgeStyled);
|