@fibery/ui-kit 1.38.0 → 1.38.2
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 +19 -0
- package/config/jest/testFrameworkSetup.js +1 -1
- package/package.json +23 -22
- package/src/abbreviate-name.ts +42 -0
- package/src/actions-menu/actions-menu-item.tsx +14 -5
- package/src/actions-menu/actions-menu-props.tsx +1 -0
- package/src/actions-menu/actions-menu.tsx +4 -1
- package/src/actions-menu/context-actions-menu.tsx +22 -0
- package/src/animated-height-container.tsx +24 -5
- package/src/antd/ant-upload.tsx +1 -1
- package/src/antd/input.tsx +4 -0
- package/src/avatar.tsx +1 -27
- package/src/button/base-button.tsx +2 -0
- package/src/button/button.tsx +4 -4
- package/src/button/icon-button.tsx +3 -3
- package/src/card-container.tsx +3 -2
- package/src/checkbox.tsx +17 -0
- package/src/collapsible/index.tsx +4 -2
- package/src/collapsible-section.tsx +3 -1
- package/src/date-picker/autocomplete.tsx +11 -5
- package/src/date-picker/date-range-input.tsx +5 -1
- package/src/date-picker/date-range-picker-popup-content.tsx +31 -13
- package/src/date-picker/single-date-input.tsx +5 -1
- package/src/date-picker/styles.ts +20 -0
- package/src/design-system.ts +26 -1
- package/src/dropdown-menu/index.tsx +11 -1
- package/src/emoji-picker/emoji-picker-preview-footer.tsx +4 -0
- package/src/emoji-picker/emoji.tsx +4 -3
- package/src/emoji-picker/primitives/emoji.tsx +49 -51
- package/src/emoji-picker/primitives/footer.tsx +6 -0
- package/src/emoji-picker/primitives/search.tsx +8 -0
- package/src/emoji-picker/primitives/skin-tone.tsx +12 -2
- package/src/fibermoji-placeholder.tsx +2 -29
- package/src/file-item-2.tsx +566 -0
- package/src/file-item.tsx +196 -162
- package/src/header-layout.tsx +11 -0
- package/src/html-styles.ts +13 -4
- package/src/icons/ast/Back.ts +1 -1
- package/src/icons/ast/ExtensionFiles.ts +1 -1
- package/src/icons/ast/FileArchive.ts +8 -0
- package/src/icons/ast/FileBroken.ts +8 -0
- package/src/icons/ast/FileDocument.ts +8 -0
- package/src/icons/ast/FileImage.ts +8 -0
- package/src/icons/ast/FilePresentation.ts +8 -0
- package/src/icons/ast/FileTable.ts +8 -0
- package/src/icons/ast/FileUpload.ts +1 -1
- package/src/icons/ast/FileVideo.ts +8 -0
- package/src/icons/ast/SwitchToMention.ts +8 -0
- package/src/icons/ast/SwitchToText.ts +8 -0
- package/src/icons/ast/SwitchToWidget.ts +8 -0
- package/src/icons/ast/ViewGallery.ts +8 -0
- package/src/icons/ast/index.tsx +11 -0
- package/src/icons/react/FileArchive.tsx +13 -0
- package/src/icons/react/FileBroken.tsx +13 -0
- package/src/icons/react/FileDocument.tsx +13 -0
- package/src/icons/react/FileImage.tsx +13 -0
- package/src/icons/react/FilePresentation.tsx +13 -0
- package/src/icons/react/FileTable.tsx +13 -0
- package/src/icons/react/FileVideo.tsx +13 -0
- package/src/icons/react/SwitchToMention.tsx +13 -0
- package/src/icons/react/SwitchToText.tsx +13 -0
- package/src/icons/react/SwitchToWidget.tsx +13 -0
- package/src/icons/react/ViewGallery.tsx +13 -0
- package/src/icons/react/index.tsx +11 -0
- package/src/icons/svg/back.svg +3 -1
- package/src/icons/svg/extension/files.svg +3 -1
- package/src/icons/svg/file-archive.svg +3 -0
- package/src/icons/svg/file-broken.svg +3 -0
- package/src/icons/svg/file-document.svg +3 -0
- package/src/icons/svg/file-image.svg +3 -0
- package/src/icons/svg/file-presentation.svg +3 -0
- package/src/icons/svg/file-table.svg +3 -0
- package/src/icons/svg/file-upload.svg +3 -6
- package/src/icons/svg/file-video.svg +3 -0
- package/src/icons/svg/switch-to-mention.svg +3 -0
- package/src/icons/svg/switch-to-text.svg +4 -0
- package/src/icons/svg/switch-to-widget.svg +4 -0
- package/src/icons/svg/view-gallery.svg +4 -0
- package/src/images-gallery/images-gallery-fall-through-provider.tsx +11 -0
- package/src/images-gallery/images-gallery.tsx +13 -10
- package/src/images-gallery/zoom.tsx +13 -2
- package/src/images-gallery-2/images-gallery-2.tsx +522 -0
- package/src/images-gallery-2/slide-buttons.tsx +271 -0
- package/src/images-gallery-2/zoom.tsx +35 -0
- package/src/kbd.tsx +46 -0
- package/src/loading-sausage.tsx +2 -2
- package/src/modal-menu/modal-menu-item.tsx +10 -2
- package/src/modal-menu/modal-menu-trigger.tsx +19 -2
- package/src/notification-dot.tsx +16 -7
- package/src/platform-symbols.ts +9 -0
- package/src/platform.ts +2 -0
- package/src/popover/index.tsx +31 -8
- package/src/popover/modifiers.tsx +26 -19
- package/src/reactions/reaction-button.tsx +323 -101
- package/src/reactions/reaction-picker.tsx +14 -1
- package/src/reactions/reactions.tsx +11 -1
- package/src/reactions/tootltip.tsx +55 -0
- package/src/rich-text-embed-error.tsx +2 -1
- package/src/select/components/group-heading.tsx +8 -0
- package/src/select/components/menu-list-virtualized.tsx +6 -2
- package/src/select/components/option.tsx +4 -0
- package/src/select/select-control-settings-context.tsx +11 -2
- package/src/select/select-in-popover.tsx +10 -7
- package/src/select/styles.ts +21 -1
- package/src/tab-nav/tab-nav.tsx +1 -1
- package/src/toggle.tsx +8 -0
- package/src/tooltip.tsx +28 -19
- package/src/type-badge.tsx +5 -2
- package/src/unit/unit-with-tooltip.tsx +1 -1
- package/src/use-is-phone.tsx +34 -6
- package/src/shortcut-badge.tsx +0 -29
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {PhotoSwipe} from "photoswipe/lightbox";
|
|
2
|
+
import {useEffect, useMemo, useState} from "react";
|
|
3
|
+
import {css} from "@linaria/core";
|
|
4
|
+
import {IconButton} from "../button/icon-button";
|
|
5
|
+
import Back from "../icons/react/Back";
|
|
6
|
+
import ArrowForward from "../icons/react/ArrowForward";
|
|
7
|
+
import LoupeZoomIn from "../icons/react/LoupeZoomIn";
|
|
8
|
+
import LoupeZoomOut from "../icons/react/LoupeZoomOut";
|
|
9
|
+
import RicheditorOpenLink from "../icons/react/RicheditorOpenLink";
|
|
10
|
+
import Link from "../icons/react/Link";
|
|
11
|
+
import Close from "../icons/react/Close";
|
|
12
|
+
import {colors, createInlineTheme, getThemeColors, space} from "../design-system";
|
|
13
|
+
import {safeAreaInsetRightVar, safeAreaInsetTopVar} from "../mobile-styles";
|
|
14
|
+
import {Button} from "../button/button";
|
|
15
|
+
import {copyToClipboard} from "../copy-to-clipboard";
|
|
16
|
+
import {ToastProvider, useToast} from "../toast/toast-provider";
|
|
17
|
+
import {ToastQueue} from "../toast/toast-queue";
|
|
18
|
+
import {ActionsMenu} from "../actions-menu";
|
|
19
|
+
import {ActionsButtonCompact} from "../button/actions-button-compact";
|
|
20
|
+
import {ThemeProvider} from "../theme-provider";
|
|
21
|
+
import {ThemeMode} from "../theme-settings";
|
|
22
|
+
|
|
23
|
+
const barClassName = css`
|
|
24
|
+
position: absolute;
|
|
25
|
+
top: calc(${space.s20}px + var(${safeAreaInsetTopVar}));
|
|
26
|
+
right: calc(${space.s20}px + var(${safeAreaInsetRightVar}));
|
|
27
|
+
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: row;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: flex-end; /* align content to the right */
|
|
32
|
+
gap: ${space.s8}px; /* space between children */
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const wrapper = css`
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
type SlideState = {
|
|
41
|
+
currIndex: number;
|
|
42
|
+
numItems: number;
|
|
43
|
+
src: string;
|
|
44
|
+
actions?: React.ReactNode;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function SlideButtons({
|
|
48
|
+
pswp,
|
|
49
|
+
toastQueue,
|
|
50
|
+
items,
|
|
51
|
+
uiHandlers,
|
|
52
|
+
}: {
|
|
53
|
+
pswp: PhotoSwipe;
|
|
54
|
+
toastQueue: ToastQueue;
|
|
55
|
+
themeMode: ThemeMode;
|
|
56
|
+
items: {
|
|
57
|
+
src: string;
|
|
58
|
+
actions?: React.ReactNode;
|
|
59
|
+
}[];
|
|
60
|
+
uiHandlers: {
|
|
61
|
+
showUI: () => void;
|
|
62
|
+
setForceShowUI: (value: boolean) => void;
|
|
63
|
+
};
|
|
64
|
+
}) {
|
|
65
|
+
const [slideState, setSliceState] = useState<SlideState>(getSlideData(pswp, items));
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const handler = () => {
|
|
68
|
+
setSliceState(getSlideData(pswp, items));
|
|
69
|
+
};
|
|
70
|
+
pswp.on("slideActivate", handler);
|
|
71
|
+
|
|
72
|
+
return () => {
|
|
73
|
+
pswp.off("slideActivate", handler);
|
|
74
|
+
};
|
|
75
|
+
}, [items, pswp]);
|
|
76
|
+
|
|
77
|
+
const {theme, styles} = useMemo(() => {
|
|
78
|
+
const theme = getThemeColors(colors.brandColors.blue, "dark");
|
|
79
|
+
return {theme, styles: createInlineTheme(theme, emptyArray)};
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<ToastProvider toastQueue={toastQueue}>
|
|
84
|
+
<ThemeProvider theme={theme}>
|
|
85
|
+
<div style={styles} className={wrapper}>
|
|
86
|
+
<div className={barClassName}>
|
|
87
|
+
<CopyURLButton pswp={pswp} showUI={uiHandlers.showUI} />
|
|
88
|
+
<ZoomButton pswp={pswp} showUI={uiHandlers.showUI} />
|
|
89
|
+
{slideState.actions ? (
|
|
90
|
+
<SlideActionsMenu actions={slideState.actions} uiHandlers={uiHandlers} />
|
|
91
|
+
) : (
|
|
92
|
+
<IconButton
|
|
93
|
+
size="medium"
|
|
94
|
+
color="neutral"
|
|
95
|
+
variant="soft"
|
|
96
|
+
onClick={() => {
|
|
97
|
+
window.open(slideState.src, "_blank");
|
|
98
|
+
uiHandlers.showUI();
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<RicheditorOpenLink />
|
|
102
|
+
</IconButton>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<IconButton
|
|
106
|
+
onClick={() => {
|
|
107
|
+
pswp.close();
|
|
108
|
+
uiHandlers.showUI();
|
|
109
|
+
}}
|
|
110
|
+
size="medium"
|
|
111
|
+
color="neutral"
|
|
112
|
+
variant="soft"
|
|
113
|
+
>
|
|
114
|
+
<Close />
|
|
115
|
+
</IconButton>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="pswp__button--arrow--prev pswp__button--arrow">
|
|
118
|
+
<IconButton
|
|
119
|
+
disabled={slideState.currIndex === 0}
|
|
120
|
+
onClick={() => {
|
|
121
|
+
pswp.prev();
|
|
122
|
+
uiHandlers.showUI();
|
|
123
|
+
}}
|
|
124
|
+
size="medium"
|
|
125
|
+
color="neutral"
|
|
126
|
+
variant="soft"
|
|
127
|
+
>
|
|
128
|
+
<Back />
|
|
129
|
+
</IconButton>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="pswp__button--arrow--next pswp__button--arrow">
|
|
132
|
+
<IconButton
|
|
133
|
+
disabled={slideState.currIndex === slideState.numItems - 1}
|
|
134
|
+
onClick={() => {
|
|
135
|
+
pswp.next();
|
|
136
|
+
uiHandlers.showUI();
|
|
137
|
+
}}
|
|
138
|
+
size="medium"
|
|
139
|
+
color="neutral"
|
|
140
|
+
variant="soft"
|
|
141
|
+
>
|
|
142
|
+
<ArrowForward />
|
|
143
|
+
</IconButton>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</ThemeProvider>
|
|
147
|
+
</ToastProvider>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function SlideActionsMenu({
|
|
152
|
+
actions,
|
|
153
|
+
uiHandlers,
|
|
154
|
+
}: {
|
|
155
|
+
actions?: React.ReactNode;
|
|
156
|
+
uiHandlers: {showUI: () => void; setForceShowUI: (value: boolean) => void};
|
|
157
|
+
}) {
|
|
158
|
+
const [container, setContainer] = useState<HTMLDivElement | null>();
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div>
|
|
162
|
+
<ActionsMenu
|
|
163
|
+
onOpenChange={(value) => {
|
|
164
|
+
uiHandlers.setForceShowUI(value);
|
|
165
|
+
}}
|
|
166
|
+
contentStyles={css`
|
|
167
|
+
margin-top: ${space.s8}px;
|
|
168
|
+
`}
|
|
169
|
+
container={container}
|
|
170
|
+
trigger={<ActionsButtonCompact size="medium" color="neutral" variant="soft" onClick={uiHandlers.showUI} />}
|
|
171
|
+
>
|
|
172
|
+
{actions}
|
|
173
|
+
</ActionsMenu>
|
|
174
|
+
<div ref={setContainer} />
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function ZoomButton({pswp, showUI}: {pswp: PhotoSwipe; showUI: () => void}) {
|
|
180
|
+
const [zoomed, setZoomed] = useState(false);
|
|
181
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
182
|
+
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
if (!pswp) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function handleZoomChange() {
|
|
189
|
+
const slide = pswp.currSlide;
|
|
190
|
+
if (!slide) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
setIsVisible(slide.zoomLevels.fit !== 1);
|
|
195
|
+
|
|
196
|
+
const zoomed = slide.currZoomLevel > slide.zoomLevels.fit;
|
|
197
|
+
setZoomed(zoomed);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
pswp.on("afterInit", handleZoomChange);
|
|
201
|
+
pswp.on("zoomPanUpdate", handleZoomChange);
|
|
202
|
+
|
|
203
|
+
return () => {
|
|
204
|
+
pswp.off("afterInit", handleZoomChange);
|
|
205
|
+
pswp.off("zoomPanUpdate", handleZoomChange);
|
|
206
|
+
};
|
|
207
|
+
}, [pswp]);
|
|
208
|
+
|
|
209
|
+
if (!isVisible) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<IconButton
|
|
215
|
+
onClick={() => {
|
|
216
|
+
setZoomed(!zoomed);
|
|
217
|
+
pswp.toggleZoom();
|
|
218
|
+
showUI();
|
|
219
|
+
}}
|
|
220
|
+
size="medium"
|
|
221
|
+
color="neutral"
|
|
222
|
+
variant="soft"
|
|
223
|
+
>
|
|
224
|
+
{zoomed ? <LoupeZoomOut /> : <LoupeZoomIn />}
|
|
225
|
+
</IconButton>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function CopyURLButton({pswp, showUI}: {pswp: PhotoSwipe; showUI: () => void}) {
|
|
230
|
+
const toast = useToast();
|
|
231
|
+
return (
|
|
232
|
+
<Button
|
|
233
|
+
onClick={() => {
|
|
234
|
+
showUI();
|
|
235
|
+
if (!pswp?.currSlide?.data.src) {
|
|
236
|
+
toast.error({title: "Unable to copy."});
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const src = new URL(pswp.currSlide.data.src, `${window.location}`).href;
|
|
241
|
+
if (src) {
|
|
242
|
+
copyToClipboard(src)
|
|
243
|
+
.then(() => {
|
|
244
|
+
toast.success({title: "Copied!"});
|
|
245
|
+
})
|
|
246
|
+
.catch(() => {
|
|
247
|
+
toast.error({title: "Unable to copy."});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}}
|
|
251
|
+
size="medium"
|
|
252
|
+
variant="soft"
|
|
253
|
+
color="neutral"
|
|
254
|
+
iconStart={<Link />}
|
|
255
|
+
type="button"
|
|
256
|
+
>
|
|
257
|
+
Copy file URL
|
|
258
|
+
</Button>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const emptyArray: string[] = [];
|
|
263
|
+
|
|
264
|
+
function getSlideData(pswp: PhotoSwipe, items: {src: string; actions?: React.ReactNode}[]) {
|
|
265
|
+
return {
|
|
266
|
+
currIndex: pswp.currIndex,
|
|
267
|
+
numItems: pswp.getNumItems(),
|
|
268
|
+
src: items[pswp.currIndex].src,
|
|
269
|
+
actions: items[pswp.currIndex].actions,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, {ReactNode, useCallback} from "react";
|
|
2
|
+
import "react-medium-image-zoom/dist/styles.css";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
zoomed: boolean;
|
|
6
|
+
onZoomedChange: (value: boolean) => void;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
openOnClick?: boolean;
|
|
10
|
+
zoomImg?: {src: string; alt?: string};
|
|
11
|
+
actions?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const ZoomV2 = (props: Props) => {
|
|
15
|
+
const {zoomed, onZoomedChange, children, className, openOnClick} = props;
|
|
16
|
+
const onClick = useCallback(() => {
|
|
17
|
+
if (zoomed) {
|
|
18
|
+
onZoomedChange(false);
|
|
19
|
+
} else if (openOnClick) {
|
|
20
|
+
onZoomedChange(true);
|
|
21
|
+
}
|
|
22
|
+
}, [zoomed, onZoomedChange, openOnClick]);
|
|
23
|
+
|
|
24
|
+
const onDoubleClick = useCallback(() => {
|
|
25
|
+
if (!zoomed) {
|
|
26
|
+
onZoomedChange(true);
|
|
27
|
+
}
|
|
28
|
+
}, [zoomed, onZoomedChange]);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className={className} onClick={onClick} onDoubleClick={openOnClick ? undefined : onDoubleClick}>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
package/src/kbd.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {css, cx} from "@linaria/core";
|
|
2
|
+
import {border, lineHeight, space, textStyles, themeVars} from "./design-system";
|
|
3
|
+
import {useIsSupportHardwareKeyboard} from "./use-is-support-hardware-keyboard";
|
|
4
|
+
|
|
5
|
+
export const kbdCss = css`
|
|
6
|
+
${{
|
|
7
|
+
...textStyles.small,
|
|
8
|
+
...textStyles.numeric.tabularOn,
|
|
9
|
+
}}
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
min-width: ${space.s18}px;
|
|
14
|
+
height: ${space.s18}px;
|
|
15
|
+
padding: ${space.s2}px;
|
|
16
|
+
border-radius: ${border.radius4}px;
|
|
17
|
+
border: ${themeVars.shortcutBorder};
|
|
18
|
+
color: ${themeVars.shortcutTextColor};
|
|
19
|
+
line-height: ${lineHeight.nowrap};
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
user-select: none;
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const Kbd = ({className, ...props}: React.ComponentProps<"kbd">): JSX.Element | null => {
|
|
25
|
+
const isSupportHardwareKeyboard = useIsSupportHardwareKeyboard();
|
|
26
|
+
if (!isSupportHardwareKeyboard) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return <kbd className={cx(kbdCss, className)} {...props} />;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const KbdGroup = ({className, ...props}: React.ComponentProps<"kbd">) => {
|
|
33
|
+
return (
|
|
34
|
+
<kbd
|
|
35
|
+
className={cx(
|
|
36
|
+
className,
|
|
37
|
+
css`
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: ${space.s2}px;
|
|
41
|
+
`
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
};
|
package/src/loading-sausage.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import {css} from "@linaria/core";
|
|
|
2
2
|
import {styled} from "@linaria/react";
|
|
3
3
|
import cn from "classnames";
|
|
4
4
|
import _ from "lodash";
|
|
5
|
-
import {memo, useRef} from "react";
|
|
5
|
+
import {ComponentType, memo, ReactNode, useRef} from "react";
|
|
6
6
|
import {space, themeVars} from "./design-system";
|
|
7
7
|
|
|
8
8
|
const getRandomWithSeed = _.memoize((seed: number) => {
|
|
@@ -141,7 +141,7 @@ type SausageListProps = {
|
|
|
141
141
|
amount?: number;
|
|
142
142
|
minAmount?: number;
|
|
143
143
|
maxAmount?: number;
|
|
144
|
-
WrapperComponent?:
|
|
144
|
+
WrapperComponent?: ComponentType<{children: ReactNode}> | null;
|
|
145
145
|
} & Omit<LoadingSausageProps, "index">;
|
|
146
146
|
export const LoadingSausageList = memo<SausageListProps>(function LoadingSausageList({
|
|
147
147
|
amount,
|
|
@@ -4,11 +4,12 @@ import {space, textStyles} from "../design-system";
|
|
|
4
4
|
import {useModalMenu} from "./contexts/modal-menu-context";
|
|
5
5
|
|
|
6
6
|
interface ModalMenuItemProps extends Omit<ComponentProps<"div">, "onSelect"> {
|
|
7
|
+
disabled?: boolean;
|
|
7
8
|
onSelect?: (event: Event) => void;
|
|
8
9
|
children?: ReactNode;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
export const ModalMenuItem = ({children, onSelect, ...otherProps}: ModalMenuItemProps) => {
|
|
12
|
+
export const ModalMenuItem = ({children, disabled, onSelect, ...otherProps}: ModalMenuItemProps) => {
|
|
12
13
|
const {onOpenChange} = useModalMenu();
|
|
13
14
|
|
|
14
15
|
return (
|
|
@@ -20,10 +21,17 @@ export const ModalMenuItem = ({children, onSelect, ...otherProps}: ModalMenuItem
|
|
|
20
21
|
css`
|
|
21
22
|
${textStyles.big}
|
|
22
23
|
padding: ${space.s10}px ${space.s16}px ${space.s10}px ${space.s16}px;
|
|
23
|
-
|
|
24
|
+
`,
|
|
25
|
+
disabled &&
|
|
26
|
+
css`
|
|
27
|
+
opacity: 0.4;
|
|
28
|
+
`
|
|
24
29
|
)}
|
|
25
30
|
onClick={(event) => {
|
|
26
31
|
otherProps.onClick?.(event);
|
|
32
|
+
if (disabled) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
27
35
|
const selectionEvent = new CustomEvent("modal-menu-select", {bubbles: true, cancelable: true});
|
|
28
36
|
onSelect?.(selectionEvent);
|
|
29
37
|
if (!selectionEvent.defaultPrevented) {
|
|
@@ -4,11 +4,20 @@ import {useModalMenu} from "./contexts/modal-menu-context";
|
|
|
4
4
|
|
|
5
5
|
interface ModalMenuTriggerProps extends ComponentProps<"div"> {
|
|
6
6
|
asChild?: boolean;
|
|
7
|
+
triggerEvent?: "click" | "context-menu";
|
|
7
8
|
children: ReactHTMLElement<HTMLDivElement> | ReactNode;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export const ModalMenuTrigger = ({
|
|
11
|
+
export const ModalMenuTrigger = ({
|
|
12
|
+
asChild,
|
|
13
|
+
triggerEvent,
|
|
14
|
+
children,
|
|
15
|
+
onClick,
|
|
16
|
+
onContextMenu,
|
|
17
|
+
...otherProps
|
|
18
|
+
}: ModalMenuTriggerProps) => {
|
|
11
19
|
const {open, onOpenChange} = useModalMenu();
|
|
20
|
+
|
|
12
21
|
if (asChild) {
|
|
13
22
|
const className = (children as ReactHTMLElement<HTMLDivElement>)?.props?.className;
|
|
14
23
|
return cloneElement(children as ReactHTMLElement<HTMLDivElement>, {
|
|
@@ -16,7 +25,15 @@ export const ModalMenuTrigger = ({asChild, children, onClick, ...otherProps}: Mo
|
|
|
16
25
|
className: cx(className, otherProps.className),
|
|
17
26
|
onClick: (event) => {
|
|
18
27
|
onClick?.(event);
|
|
19
|
-
|
|
28
|
+
if (triggerEvent === "click") {
|
|
29
|
+
onOpenChange(!open);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
onContextMenu: (event) => {
|
|
33
|
+
onContextMenu?.(event);
|
|
34
|
+
if (triggerEvent === "context-menu") {
|
|
35
|
+
onOpenChange(!open);
|
|
36
|
+
}
|
|
20
37
|
},
|
|
21
38
|
});
|
|
22
39
|
}
|
package/src/notification-dot.tsx
CHANGED
|
@@ -10,14 +10,23 @@ const activityIndicatorStyle = css`
|
|
|
10
10
|
}}
|
|
11
11
|
`;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
type PropsType = {
|
|
14
|
+
big?: boolean;
|
|
15
|
+
color?: string;
|
|
16
|
+
style?: Record<string, string | number | null>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const NotificationDot = ({big = true, color = themeVars.active, style}: PropsType) => {
|
|
14
20
|
const size = big ? space.s6 : space.s4;
|
|
15
21
|
const marginTop = big ? -6 : -1;
|
|
16
22
|
const marginRight = big ? -1 : 0;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
const styleObject = {
|
|
24
|
+
backgroundColor: color,
|
|
25
|
+
width: size,
|
|
26
|
+
height: size,
|
|
27
|
+
marginTop,
|
|
28
|
+
marginRight,
|
|
29
|
+
...style,
|
|
30
|
+
};
|
|
31
|
+
return <div className={activityIndicatorStyle} style={styleObject} />;
|
|
23
32
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {isMac} from "./platform";
|
|
2
|
+
|
|
3
|
+
export const ctrlSymbol = isMac ? "⌘" : "Ctrl";
|
|
4
|
+
export const explicitCtrlSymbol = isMac ? "⌃" : "Ctrl";
|
|
5
|
+
export const winSymbol = isMac ? "⌘" : "Win";
|
|
6
|
+
export const shiftSymbol = isMac ? "⇧" : "⇧";
|
|
7
|
+
export const altSymbol = isMac ? "⌥" : "Alt";
|
|
8
|
+
export const deleteSymbol = isMac ? "⌫" : "Delete";
|
|
9
|
+
export const enterSymbol = isMac ? "↵" : "↵";
|
package/src/platform.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {UAParser} from "ua-parser-js";
|
|
2
2
|
|
|
3
3
|
export const isMac = navigator.platform.startsWith("Mac");
|
|
4
|
+
export const isWindows = navigator.platform.startsWith("Win");
|
|
5
|
+
export const isLinux = navigator.platform.startsWith("Linux");
|
|
4
6
|
export const isMobile = new UAParser(navigator.userAgent).getDevice().type === "mobile";
|
package/src/popover/index.tsx
CHANGED
|
@@ -36,7 +36,7 @@ import {useTheme} from "../theme-provider";
|
|
|
36
36
|
import {$TSFixMe} from "../tsfixme";
|
|
37
37
|
import {useIsPhone} from "../use-is-phone";
|
|
38
38
|
import {usePopupModifiers} from "./popup-modifiers-context";
|
|
39
|
-
import {
|
|
39
|
+
import {applyMaxSize, makeMaxSizeModifier, observeWidth} from "./modifiers";
|
|
40
40
|
import {isElementReference, Reference} from "./reference";
|
|
41
41
|
import {basicPopupClassName, basicPopupContainerClassName, hidePopupWhenTriggerIsHiddenClassName} from "./styles";
|
|
42
42
|
import {MobilePopupProvider, useMobilePopup} from "./mobile-popup-context";
|
|
@@ -249,13 +249,13 @@ const Trigger = ({asChild = false, reference, children}: TriggerProps) => {
|
|
|
249
249
|
}
|
|
250
250
|
}, [reference, setTriggerElement]);
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
// Reference is passed
|
|
253
|
+
if (reference !== undefined) {
|
|
253
254
|
return null;
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
if (asChild && isValidElement(children)) {
|
|
257
258
|
return cloneElement(children, {
|
|
258
|
-
// @ts-expect-error: React.cloneElement typing does not include `ref`, but it's valid at runtime
|
|
259
259
|
ref: setTriggerElement,
|
|
260
260
|
"aria-expanded": open,
|
|
261
261
|
"aria-haspopup": true,
|
|
@@ -307,6 +307,8 @@ type ContentProps = {
|
|
|
307
307
|
|
|
308
308
|
export type ContentRef = {update: (() => Promise<unknown>) | null};
|
|
309
309
|
|
|
310
|
+
const padding = space.s8;
|
|
311
|
+
|
|
310
312
|
const Content = forwardRef<ContentRef, ContentProps>(function PopoverContentInner(
|
|
311
313
|
{
|
|
312
314
|
children,
|
|
@@ -341,23 +343,44 @@ const Content = forwardRef<ContentRef, ContentProps>(function PopoverContentInne
|
|
|
341
343
|
|
|
342
344
|
// We get infinite popup renders if this is not in a separate memo
|
|
343
345
|
const shrinkableModifiers = useMemo(() => {
|
|
344
|
-
return shrinkable
|
|
346
|
+
return shrinkable
|
|
347
|
+
? [
|
|
348
|
+
makeMaxSizeModifier({
|
|
349
|
+
padding,
|
|
350
|
+
...preventOverflowOptions,
|
|
351
|
+
}),
|
|
352
|
+
applyMaxSize,
|
|
353
|
+
]
|
|
354
|
+
: [];
|
|
345
355
|
}, [preventOverflowOptions, shrinkable]);
|
|
346
356
|
|
|
347
357
|
const modifiers = useMemo(() => {
|
|
348
358
|
return _.compact([
|
|
349
|
-
{
|
|
359
|
+
{
|
|
360
|
+
name: "flip",
|
|
361
|
+
options: {
|
|
362
|
+
padding,
|
|
363
|
+
...popupModifiers.flipOptions,
|
|
364
|
+
...flipOptions,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
350
367
|
{
|
|
351
368
|
name: "preventOverflow",
|
|
352
369
|
options: {
|
|
353
370
|
tether: false,
|
|
354
|
-
padding
|
|
371
|
+
padding,
|
|
355
372
|
...popupModifiers.preventOverflowOptions,
|
|
356
373
|
...preventOverflowOptions,
|
|
357
374
|
},
|
|
358
375
|
},
|
|
359
|
-
{
|
|
360
|
-
|
|
376
|
+
{
|
|
377
|
+
name: "offset",
|
|
378
|
+
options: {offset},
|
|
379
|
+
},
|
|
380
|
+
!transform && {
|
|
381
|
+
name: "computeStyles",
|
|
382
|
+
options: {gpuAcceleration: false},
|
|
383
|
+
},
|
|
361
384
|
observeWidth,
|
|
362
385
|
...shrinkableModifiers,
|
|
363
386
|
...additionalModifiers,
|
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import {Options as PreventOverflowOptions} from "@popperjs/core/lib/modifiers/preventOverflow";
|
|
2
|
-
import {
|
|
3
|
-
import {Modifier, ModifierArguments} from "@popperjs/core";
|
|
2
|
+
import {Modifier, ModifierArguments, Placement, Boundary, RootBoundary, Padding, Context} from "@popperjs/core";
|
|
4
3
|
import maxSize from "popper-max-size-modifier";
|
|
5
4
|
import {getResizeListener} from "@fibery/helpers/utils/resize-listener";
|
|
6
5
|
import {isElementReference} from "./reference";
|
|
7
6
|
import _ from "lodash";
|
|
8
7
|
|
|
9
|
-
type
|
|
8
|
+
type MaxSizeOptions = {
|
|
9
|
+
placement: Placement;
|
|
10
|
+
boundary: Boundary;
|
|
11
|
+
rootBoundary: RootBoundary;
|
|
12
|
+
elementContext: Context;
|
|
13
|
+
altBoundary: boolean;
|
|
14
|
+
padding: Padding;
|
|
15
|
+
};
|
|
10
16
|
|
|
11
|
-
export const
|
|
12
|
-
{
|
|
17
|
+
export const makeMaxSizeModifier = (options: Partial<PreventOverflowOptions>): Modifier<"maxSize", MaxSizeOptions> => {
|
|
18
|
+
return {
|
|
13
19
|
...maxSize,
|
|
14
|
-
options
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
20
|
+
options,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const applyMaxSize: Modifier<"applyMaxSize", Record<string, never>> = {
|
|
25
|
+
name: "applyMaxSize",
|
|
26
|
+
enabled: true,
|
|
27
|
+
phase: "beforeWrite",
|
|
28
|
+
requires: ["maxSize"],
|
|
29
|
+
fn({state}) {
|
|
30
|
+
const {width, height} = state.modifiersData.maxSize;
|
|
31
|
+
state.styles.popper.maxWidth = `${width}px`;
|
|
32
|
+
state.styles.popper.maxHeight = `${height}px`;
|
|
26
33
|
},
|
|
27
|
-
|
|
34
|
+
};
|
|
28
35
|
|
|
29
36
|
const observeWidthEffect: (arg0: ModifierArguments<Record<string, unknown>>) => void = ({state, instance}) => {
|
|
30
37
|
const {popper, reference} = state.elements;
|
|
@@ -54,7 +61,7 @@ const observeWidthEffect: (arg0: ModifierArguments<Record<string, unknown>>) =>
|
|
|
54
61
|
};
|
|
55
62
|
};
|
|
56
63
|
|
|
57
|
-
export const observeWidth:
|
|
64
|
+
export const observeWidth: Modifier<"observeWidth", Record<string, never>> = {
|
|
58
65
|
name: "observeWidth",
|
|
59
66
|
enabled: true,
|
|
60
67
|
phase: "main",
|