@fibery/ui-kit 1.38.1 → 1.39.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/CHANGELOG.md +14 -0
- package/package.json +12 -12
- package/src/button/button.tsx +1 -2
- package/src/card-container.tsx +5 -2
- package/src/collapsible/index.tsx +4 -2
- package/src/collapsible-section.tsx +1 -0
- package/src/design-system.ts +2 -1
- package/src/fibermoji-placeholder.tsx +1 -2
- package/src/file-item-2.tsx +188 -30
- package/src/file-item.tsx +172 -189
- 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/FileVideo.ts +8 -0
- package/src/icons/ast/index.tsx +7 -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/index.tsx +7 -0
- 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-video.svg +3 -0
- package/src/images-gallery-2/images-gallery-2.tsx +105 -25
- package/src/images-gallery-2/slide-buttons.tsx +99 -58
- package/src/kbd.tsx +1 -1
- package/src/loading-sausage.tsx +2 -2
- package/src/tooltip.tsx +1 -1
- package/src/use-is-phone.tsx +20 -12
- package/src/use-on-screen-keyboard-data.tsx +3 -4
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14">
|
|
2
|
+
<path d="M13.75 0C14.9926 0 16 1.00736 16 2.25V11.75C16 12.9926 14.9926 14 13.75 14H2.25C1.00736 14 0 12.9926 0 11.75V2.25C0 1.00736 1.00736 0 2.25 0H13.75ZM2.25 1.5C1.83579 1.5 1.5 1.83579 1.5 2.25V11.75C1.5 12.1642 1.83579 12.5 2.25 12.5H13.75C14.1642 12.5 14.5 12.1642 14.5 11.75V2.25C14.5 1.83579 14.1642 1.5 13.75 1.5H2.25ZM12.1465 10.1465C12.4613 10.4614 12.2383 10.9999 11.793 11H4.20703C3.76165 10.9999 3.53861 10.4615 3.85352 10.1465L6 8L7 9L9 7L12.1465 10.1465ZM12 3C12.5523 3.00002 13 3.44773 13 4C13 4.55227 12.5523 4.99998 12 5C11.4477 4.99998 11 4.55227 11 4C11 3.44773 11.4477 3.00002 12 3Z" />
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
|
2
|
+
<path d="M12.7617 16.5723L12.8184 16.6348C13.0805 16.9633 13.0612 17.4486 12.7617 17.7549C12.4622 18.0611 11.9874 18.0805 11.666 17.8125L11.6045 17.7549L10.001 16.1152L8.39746 17.7549C8.07799 18.0812 7.56066 18.0812 7.24121 17.7549C6.9217 17.4282 6.92172 16.8989 7.24121 16.5723L8.77832 15H4.25C3.00736 15 2 13.9926 2 12.75V5.25C2 4.00736 3.00736 3 4.25 3H15.75C16.9926 3 18 4.00736 18 5.25V12.75C18 13.9926 16.9926 15 15.75 15H11.2236L12.7617 16.5723ZM4.25 4.5C3.83579 4.5 3.5 4.83579 3.5 5.25V12.75C3.5 13.1642 3.83579 13.5 4.25 13.5H15.75C16.1642 13.5 16.5 13.1642 16.5 12.75V5.25C16.5 4.83579 16.1642 4.5 15.75 4.5H4.25ZM10 9H12C12 9.39556 11.8828 9.78243 11.6631 10.1113C11.4434 10.4401 11.1309 10.6963 10.7656 10.8477C10.4002 10.999 9.99734 11.0391 9.60938 10.9619C9.22155 10.8847 8.86556 10.6937 8.58594 10.4141C8.30632 10.1344 8.1153 9.77845 8.03809 9.39062C7.96092 9.00266 8.00097 8.59983 8.15234 8.23438C8.30372 7.86906 8.55988 7.55662 8.88867 7.33691C9.21757 7.11715 9.60444 7 10 7V9Z" />
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" >
|
|
2
|
+
<path d="M11.7422 1.5C11.9405 1.5 12.131 1.5787 12.2715 1.71875L16.7764 6.21582C16.9173 6.35652 16.9971 6.54787 16.9971 6.74707V9.94141C16.9986 9.96076 17 9.98026 17 10C17 10.0194 16.9985 10.0386 16.9971 10.0576V13.9414C16.9986 13.9608 17 13.9803 17 14C17 14.0194 16.9985 14.0386 16.9971 14.0576V15.75C16.9971 17.2687 15.766 18.4999 14.248 18.5H5.74512C4.22716 18.4999 2.99708 17.2687 2.99707 15.75V4.25C2.99707 2.73127 4.22713 1.50008 5.74512 1.5H11.7422ZM8.75 17H14.248C14.938 16.9999 15.4971 16.4403 15.4971 15.75V14.75H8.75V17ZM4.49609 15.75C4.4961 16.4403 5.05519 16.9999 5.74512 17H7.25V14.75H4.49609V15.75ZM8.75 13.25H15.4971V10.75H8.75V13.25ZM4.49609 13.25H7.25V10.75H4.49609V13.25ZM5.74512 3C5.05516 3.00008 4.49609 3.55969 4.49609 4.25V9.25H15.4971V7.49707H12.5752C12.1555 7.49702 11.7528 7.33009 11.4561 7.0332C11.1593 6.73627 10.9932 6.33301 10.9932 5.91309V3H5.74512ZM12.4922 5.91309C12.4922 5.93519 12.501 5.95703 12.5166 5.97266C12.5322 5.98808 12.5533 5.99702 12.5752 5.99707H14.4346L12.4922 4.05762V5.91309Z" />
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
|
2
|
+
<path d="M15.75 4C16.9926 4 18 5.00736 18 6.25V13.75C18 14.9926 16.9926 16 15.75 16H4.25C3.00736 16 2 14.9926 2 13.75V6.25C2 5.00736 3.00736 4 4.25 4H15.75ZM4.25 5.5C3.83579 5.5 3.5 5.83579 3.5 6.25V13.75C3.5 14.1642 3.83579 14.5 4.25 14.5H15.75C16.1642 14.5 16.5 14.1642 16.5 13.75V6.25C16.5 5.83579 16.1642 5.5 15.75 5.5H4.25ZM7.5 7.80859C7.50028 7.43719 7.89134 7.19532 8.22363 7.36133L12.6055 9.55273C12.974 9.737 12.974 10.263 12.6055 10.4473L8.22363 12.6377C7.89133 12.8038 7.50034 12.5628 7.5 12.1914V7.80859Z" />
|
|
3
|
+
</svg>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
1
2
|
import PhotoSwipeLightbox, {type PhotoSwipeEventsMap} from "photoswipe/lightbox";
|
|
2
3
|
import "photoswipe/style.css";
|
|
3
4
|
import {
|
|
@@ -9,17 +10,20 @@ import {PropsWithChildren, useCallback, useEffect, useMemo, useReducer, useRef,
|
|
|
9
10
|
import {css} from "@linaria/core";
|
|
10
11
|
import {createRoot} from "react-dom/client";
|
|
11
12
|
import {space, themeVars} from "../design-system";
|
|
13
|
+
import {safeAreaInsetLeftVar, safeAreaInsetRightVar, safeAreaInsetTopVar} from "../mobile-styles";
|
|
12
14
|
import {useToast} from "../toast/toast-provider";
|
|
13
15
|
import {ToastQueue} from "../toast/toast-queue";
|
|
14
16
|
import {useThemeMode} from "../theme-provider";
|
|
15
17
|
import {ThemeMode} from "../theme-settings";
|
|
16
18
|
import {SlideButtons} from "./slide-buttons";
|
|
19
|
+
import {useIsPhone} from "../use-is-phone";
|
|
17
20
|
|
|
18
21
|
type Registry = Map<
|
|
19
22
|
string,
|
|
20
23
|
{
|
|
21
24
|
src: string;
|
|
22
25
|
type: "image" | "video" | "iframe";
|
|
26
|
+
contentType?: string;
|
|
23
27
|
actions?: React.ReactNode;
|
|
24
28
|
zoomed: boolean;
|
|
25
29
|
setZoomed: (zoomed: boolean) => void;
|
|
@@ -37,13 +41,13 @@ type Methods = {
|
|
|
37
41
|
const galleryMainClass = css`
|
|
38
42
|
z-index: 1000;
|
|
39
43
|
& .pswp__button--arrow--prev {
|
|
40
|
-
left: ${space.s20}px;
|
|
44
|
+
left: calc(${space.s20}px + var(${safeAreaInsetLeftVar}));
|
|
41
45
|
height: ${space.s28}px;
|
|
42
46
|
width: ${space.s28}px;
|
|
43
47
|
margin-top: -${space.s14}px;
|
|
44
48
|
}
|
|
45
49
|
& .pswp__button--arrow--next {
|
|
46
|
-
right: ${space.s20}px;
|
|
50
|
+
right: calc(${space.s20}px + var(${safeAreaInsetRightVar}));
|
|
47
51
|
height: ${space.s28}px;
|
|
48
52
|
width: ${space.s28}px;
|
|
49
53
|
margin-top: -${space.s14}px;
|
|
@@ -73,7 +77,7 @@ const galleryMainClass = css`
|
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
& .pswp__iframe-container {
|
|
76
|
-
padding:
|
|
80
|
+
padding: ${space.s60}px;
|
|
77
81
|
display: flex;
|
|
78
82
|
justify-content: center;
|
|
79
83
|
align-items: center;
|
|
@@ -82,13 +86,17 @@ const galleryMainClass = css`
|
|
|
82
86
|
& .pswp__iframe-container iframe {
|
|
83
87
|
width: 100%;
|
|
84
88
|
height: 100%;
|
|
85
|
-
max-width: min(822px, calc(100vw - 40px));
|
|
86
89
|
object-fit: contain;
|
|
87
90
|
display: block;
|
|
88
91
|
color: ${themeVars.textColor};
|
|
89
92
|
background-color: ${themeVars.colorBgPopup};
|
|
90
93
|
border: none;
|
|
91
94
|
}
|
|
95
|
+
|
|
96
|
+
& .pswp__top-bar {
|
|
97
|
+
top: var(${safeAreaInsetTopVar});
|
|
98
|
+
left: var(${safeAreaInsetLeftVar});
|
|
99
|
+
}
|
|
92
100
|
`;
|
|
93
101
|
|
|
94
102
|
export function ImagesGalleryProvider2({children}: PropsWithChildren) {
|
|
@@ -132,10 +140,11 @@ export function ImagesGalleryProvider2({children}: PropsWithChildren) {
|
|
|
132
140
|
|
|
133
141
|
const toast = useToast();
|
|
134
142
|
const themeMode = useThemeMode();
|
|
143
|
+
const isPhone = useIsPhone();
|
|
135
144
|
|
|
136
145
|
useEffect(() => {
|
|
137
146
|
if (galleryState.activated) {
|
|
138
|
-
const value = initGallery(imagesRegistry, toast, themeMode);
|
|
147
|
+
const value = initGallery(imagesRegistry, {toastQueue: toast, isPhone, themeMode});
|
|
139
148
|
value.on("close", () => {
|
|
140
149
|
dispatchState({type: "set-activated", payload: false});
|
|
141
150
|
markAllNotZoomed();
|
|
@@ -149,7 +158,7 @@ export function ImagesGalleryProvider2({children}: PropsWithChildren) {
|
|
|
149
158
|
lightbox.current?.destroy();
|
|
150
159
|
lightbox.current = null;
|
|
151
160
|
};
|
|
152
|
-
}, [galleryState.activated, imagesRegistry, markAllNotZoomed, toast, themeMode]);
|
|
161
|
+
}, [galleryState.activated, imagesRegistry, markAllNotZoomed, toast, themeMode, isPhone]);
|
|
153
162
|
|
|
154
163
|
return (
|
|
155
164
|
<ImagesGalleryRegistryProvider value={imagesRegistry}>
|
|
@@ -183,6 +192,7 @@ function imagesGalleryReducer(state: State, action: Actions) {
|
|
|
183
192
|
type GalleryEntry = {
|
|
184
193
|
type: string;
|
|
185
194
|
src: string;
|
|
195
|
+
contentType: string | null;
|
|
186
196
|
zoomed: boolean;
|
|
187
197
|
setZoomed: (value: boolean) => void;
|
|
188
198
|
state: "none" | "loading" | "error" | "loaded";
|
|
@@ -190,11 +200,15 @@ type GalleryEntry = {
|
|
|
190
200
|
height?: number;
|
|
191
201
|
};
|
|
192
202
|
|
|
193
|
-
function initGallery(
|
|
203
|
+
function initGallery(
|
|
204
|
+
imagesRegistry: Registry,
|
|
205
|
+
{toastQueue, themeMode, isPhone}: {toastQueue: ToastQueue; themeMode: ThemeMode; isPhone: boolean}
|
|
206
|
+
) {
|
|
194
207
|
const items = [...imagesRegistry.values()];
|
|
195
208
|
const dataSource: GalleryEntry[] = items.map((item) => ({
|
|
196
209
|
...item,
|
|
197
210
|
state: "none",
|
|
211
|
+
contentType: item.contentType ?? null,
|
|
198
212
|
}));
|
|
199
213
|
|
|
200
214
|
const zoomedIndex = dataSource.findIndex((item) => item.zoomed);
|
|
@@ -293,6 +307,7 @@ function initGallery(imagesRegistry: Registry, toastQueue: ToastQueue, themeMode
|
|
|
293
307
|
item,
|
|
294
308
|
refreshSlide,
|
|
295
309
|
getPswp: () => lightbox.pswp,
|
|
310
|
+
isPhone,
|
|
296
311
|
};
|
|
297
312
|
|
|
298
313
|
switch (item.type) {
|
|
@@ -319,10 +334,14 @@ function initGallery(imagesRegistry: Registry, toastQueue: ToastQueue, themeMode
|
|
|
319
334
|
});
|
|
320
335
|
|
|
321
336
|
const HIDE_UI_TIMEOUT_MS = 3000;
|
|
337
|
+
let forceShowUI = false;
|
|
322
338
|
let hideTimeout: ReturnType<typeof setTimeout>;
|
|
323
339
|
let isHidden = false;
|
|
324
340
|
|
|
325
341
|
function hideUI() {
|
|
342
|
+
if (forceShowUI) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
326
345
|
lightbox.pswp?.element?.classList.remove("pswp--ui-visible");
|
|
327
346
|
lightbox.pswp?.element?.classList.add("pswp--hide-cursor");
|
|
328
347
|
isHidden = true;
|
|
@@ -351,6 +370,17 @@ function initGallery(imagesRegistry: Registry, toastQueue: ToastQueue, themeMode
|
|
|
351
370
|
hideTimeout = setTimeout(hideUI, HIDE_UI_TIMEOUT_MS);
|
|
352
371
|
});
|
|
353
372
|
|
|
373
|
+
const uiHandlers = {
|
|
374
|
+
showUI,
|
|
375
|
+
setForceShowUI: (value: boolean) => {
|
|
376
|
+
forceShowUI = value;
|
|
377
|
+
// resets timer
|
|
378
|
+
if (!forceShowUI) {
|
|
379
|
+
showUI();
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
|
|
354
384
|
lightbox.on("uiRegister", () => {
|
|
355
385
|
lightbox.pswp?.ui?.registerElement({
|
|
356
386
|
name: "custom-nav",
|
|
@@ -358,7 +388,15 @@ function initGallery(imagesRegistry: Registry, toastQueue: ToastQueue, themeMode
|
|
|
358
388
|
isButton: false,
|
|
359
389
|
appendTo: "wrapper",
|
|
360
390
|
onInit: (el, pswp) => {
|
|
361
|
-
createRoot(el).render(
|
|
391
|
+
createRoot(el).render(
|
|
392
|
+
<SlideButtons
|
|
393
|
+
items={items}
|
|
394
|
+
pswp={pswp}
|
|
395
|
+
toastQueue={toastQueue}
|
|
396
|
+
themeMode={themeMode}
|
|
397
|
+
uiHandlers={uiHandlers}
|
|
398
|
+
/>
|
|
399
|
+
);
|
|
362
400
|
},
|
|
363
401
|
});
|
|
364
402
|
});
|
|
@@ -379,33 +417,62 @@ type ContentLoadArg = {
|
|
|
379
417
|
item: GalleryEntry;
|
|
380
418
|
refreshSlide: (index: number) => void;
|
|
381
419
|
getPswp: () => PhotoSwipeLightbox["pswp"];
|
|
420
|
+
isPhone: boolean;
|
|
382
421
|
};
|
|
383
422
|
|
|
384
423
|
function onImageContentLoad({e, item, refreshSlide}: ContentLoadArg) {
|
|
385
424
|
item.state = "loading";
|
|
386
|
-
|
|
425
|
+
// we ok if content type request fails
|
|
426
|
+
const contentTypePromise = item.contentType
|
|
427
|
+
? Promise.resolve(item.contentType)
|
|
428
|
+
: fetch(item.src, {method: "HEAD"})
|
|
429
|
+
.then((response) => response.headers.get("content-type"))
|
|
430
|
+
.catch();
|
|
431
|
+
|
|
432
|
+
const contentPromise = new Promise<{width: number; height: number}>((resolve, reject) => {
|
|
433
|
+
const onLoad = () => {
|
|
434
|
+
resolve({
|
|
435
|
+
width: img.naturalWidth,
|
|
436
|
+
height: img.naturalHeight,
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const img = new Image();
|
|
441
|
+
img.src = item.src;
|
|
442
|
+
if (img.complete) {
|
|
443
|
+
onLoad();
|
|
444
|
+
}
|
|
445
|
+
img.onload = () => onLoad();
|
|
446
|
+
img.onerror = () => {
|
|
447
|
+
reject("error");
|
|
448
|
+
};
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
Promise.allSettled([contentTypePromise, contentPromise]).then(([contentTypeResult, contentResult]) => {
|
|
452
|
+
if (contentResult.status === "rejected") {
|
|
453
|
+
item.state = "error";
|
|
454
|
+
refreshSlide(e.content.index);
|
|
455
|
+
e.content.onError();
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
387
459
|
item.state = "loaded";
|
|
388
|
-
item.width =
|
|
389
|
-
item.height =
|
|
460
|
+
item.width = contentResult.value.width;
|
|
461
|
+
item.height = contentResult.value.height;
|
|
462
|
+
|
|
463
|
+
// normalize
|
|
464
|
+
if (contentTypeResult.status === "fulfilled" && contentTypeResult.value?.startsWith("image/svg")) {
|
|
465
|
+
const scale = Math.min(3000 / item.width!, 3000 / item.height!);
|
|
466
|
+
item.width = Math.ceil(scale * item.width!);
|
|
467
|
+
item.height = Math.ceil(scale * item.height!);
|
|
468
|
+
}
|
|
390
469
|
|
|
391
470
|
refreshSlide(e.content.index);
|
|
392
471
|
e.content.onLoaded();
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
const img = new Image();
|
|
396
|
-
img.src = item.src;
|
|
397
|
-
if (img.complete) {
|
|
398
|
-
onLoad();
|
|
399
|
-
}
|
|
400
|
-
img.onload = () => onLoad();
|
|
401
|
-
img.onerror = () => {
|
|
402
|
-
item.state = "error";
|
|
403
|
-
refreshSlide(e.content.index);
|
|
404
|
-
e.content.onError();
|
|
405
|
-
};
|
|
472
|
+
});
|
|
406
473
|
}
|
|
407
474
|
|
|
408
|
-
function onVideoContentLoad({e, item, refreshSlide, getPswp}: ContentLoadArg) {
|
|
475
|
+
function onVideoContentLoad({e, item, refreshSlide, getPswp, isPhone}: ContentLoadArg) {
|
|
409
476
|
item.state = "loaded";
|
|
410
477
|
|
|
411
478
|
e.content.element = document.createElement("div");
|
|
@@ -422,6 +489,19 @@ function onVideoContentLoad({e, item, refreshSlide, getPswp}: ContentLoadArg) {
|
|
|
422
489
|
refreshSlide(e.content.index);
|
|
423
490
|
e.content.onError();
|
|
424
491
|
};
|
|
492
|
+
video.onclick = (e) => {
|
|
493
|
+
e.preventDefault();
|
|
494
|
+
e.stopPropagation();
|
|
495
|
+
if (isPhone) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (video.paused) {
|
|
499
|
+
video.play().catch();
|
|
500
|
+
} else {
|
|
501
|
+
video.pause();
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
425
505
|
e.content.element.appendChild(video);
|
|
426
506
|
}
|
|
427
507
|
|
|
@@ -10,6 +10,7 @@ import RicheditorOpenLink from "../icons/react/RicheditorOpenLink";
|
|
|
10
10
|
import Link from "../icons/react/Link";
|
|
11
11
|
import Close from "../icons/react/Close";
|
|
12
12
|
import {colors, createInlineTheme, getThemeColors, space} from "../design-system";
|
|
13
|
+
import {safeAreaInsetRightVar, safeAreaInsetTopVar} from "../mobile-styles";
|
|
13
14
|
import {Button} from "../button/button";
|
|
14
15
|
import {copyToClipboard} from "../copy-to-clipboard";
|
|
15
16
|
import {ToastProvider, useToast} from "../toast/toast-provider";
|
|
@@ -21,8 +22,8 @@ import {ThemeMode} from "../theme-settings";
|
|
|
21
22
|
|
|
22
23
|
const barClassName = css`
|
|
23
24
|
position: absolute;
|
|
24
|
-
top: ${space.s20}px;
|
|
25
|
-
right: ${space.s20}px;
|
|
25
|
+
top: calc(${space.s20}px + var(${safeAreaInsetTopVar}));
|
|
26
|
+
right: calc(${space.s20}px + var(${safeAreaInsetRightVar}));
|
|
26
27
|
|
|
27
28
|
display: flex;
|
|
28
29
|
flex-direction: row;
|
|
@@ -31,6 +32,11 @@ const barClassName = css`
|
|
|
31
32
|
gap: ${space.s8}px; /* space between children */
|
|
32
33
|
`;
|
|
33
34
|
|
|
35
|
+
const wrapper = css`
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
`;
|
|
39
|
+
|
|
34
40
|
type SlideState = {
|
|
35
41
|
currIndex: number;
|
|
36
42
|
numItems: number;
|
|
@@ -41,8 +47,8 @@ type SlideState = {
|
|
|
41
47
|
export function SlideButtons({
|
|
42
48
|
pswp,
|
|
43
49
|
toastQueue,
|
|
44
|
-
themeMode,
|
|
45
50
|
items,
|
|
51
|
+
uiHandlers,
|
|
46
52
|
}: {
|
|
47
53
|
pswp: PhotoSwipe;
|
|
48
54
|
toastQueue: ToastQueue;
|
|
@@ -51,6 +57,10 @@ export function SlideButtons({
|
|
|
51
57
|
src: string;
|
|
52
58
|
actions?: React.ReactNode;
|
|
53
59
|
}[];
|
|
60
|
+
uiHandlers: {
|
|
61
|
+
showUI: () => void;
|
|
62
|
+
setForceShowUI: (value: boolean) => void;
|
|
63
|
+
};
|
|
54
64
|
}) {
|
|
55
65
|
const [slideState, setSliceState] = useState<SlideState>(getSlideData(pswp, items));
|
|
56
66
|
useEffect(() => {
|
|
@@ -64,80 +74,109 @@ export function SlideButtons({
|
|
|
64
74
|
};
|
|
65
75
|
}, [items, pswp]);
|
|
66
76
|
|
|
77
|
+
const {theme, styles} = useMemo(() => {
|
|
78
|
+
const theme = getThemeColors(colors.brandColors.blue, "dark");
|
|
79
|
+
return {theme, styles: createInlineTheme(theme, emptyArray)};
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
67
82
|
return (
|
|
68
83
|
<ToastProvider toastQueue={toastQueue}>
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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>
|
|
111
147
|
</ToastProvider>
|
|
112
148
|
);
|
|
113
149
|
}
|
|
114
150
|
|
|
115
|
-
function SlideActionsMenu({
|
|
151
|
+
function SlideActionsMenu({
|
|
152
|
+
actions,
|
|
153
|
+
uiHandlers,
|
|
154
|
+
}: {
|
|
155
|
+
actions?: React.ReactNode;
|
|
156
|
+
uiHandlers: {showUI: () => void; setForceShowUI: (value: boolean) => void};
|
|
157
|
+
}) {
|
|
116
158
|
const [container, setContainer] = useState<HTMLDivElement | null>();
|
|
117
|
-
const {theme, styles} = useMemo(() => {
|
|
118
|
-
const theme = getThemeColors(colors.brandColors.blue, themeMode === "dark" ? "light" : "dark");
|
|
119
|
-
return {theme, styles: createInlineTheme(theme, emptyArray)};
|
|
120
|
-
}, [themeMode]);
|
|
121
159
|
|
|
122
160
|
return (
|
|
123
161
|
<div>
|
|
124
162
|
<ActionsMenu
|
|
163
|
+
onOpenChange={(value) => {
|
|
164
|
+
uiHandlers.setForceShowUI(value);
|
|
165
|
+
}}
|
|
125
166
|
contentStyles={css`
|
|
126
167
|
margin-top: ${space.s8}px;
|
|
127
168
|
`}
|
|
128
169
|
container={container}
|
|
129
|
-
trigger={<ActionsButtonCompact size="medium" color="neutral" variant="
|
|
170
|
+
trigger={<ActionsButtonCompact size="medium" color="neutral" variant="soft" onClick={uiHandlers.showUI} />}
|
|
130
171
|
>
|
|
131
172
|
{actions}
|
|
132
173
|
</ActionsMenu>
|
|
133
|
-
<
|
|
134
|
-
<div style={styles} ref={setContainer}></div>
|
|
135
|
-
</ThemeProvider>
|
|
174
|
+
<div ref={setContainer} />
|
|
136
175
|
</div>
|
|
137
176
|
);
|
|
138
177
|
}
|
|
139
178
|
|
|
140
|
-
function ZoomButton({pswp}: {pswp: PhotoSwipe}) {
|
|
179
|
+
function ZoomButton({pswp, showUI}: {pswp: PhotoSwipe; showUI: () => void}) {
|
|
141
180
|
const [zoomed, setZoomed] = useState(false);
|
|
142
181
|
const [isVisible, setIsVisible] = useState(false);
|
|
143
182
|
|
|
@@ -176,21 +215,23 @@ function ZoomButton({pswp}: {pswp: PhotoSwipe}) {
|
|
|
176
215
|
onClick={() => {
|
|
177
216
|
setZoomed(!zoomed);
|
|
178
217
|
pswp.toggleZoom();
|
|
218
|
+
showUI();
|
|
179
219
|
}}
|
|
180
220
|
size="medium"
|
|
181
221
|
color="neutral"
|
|
182
|
-
variant="
|
|
222
|
+
variant="soft"
|
|
183
223
|
>
|
|
184
224
|
{zoomed ? <LoupeZoomOut /> : <LoupeZoomIn />}
|
|
185
225
|
</IconButton>
|
|
186
226
|
);
|
|
187
227
|
}
|
|
188
228
|
|
|
189
|
-
function CopyURLButton({pswp}: {pswp: PhotoSwipe}) {
|
|
229
|
+
function CopyURLButton({pswp, showUI}: {pswp: PhotoSwipe; showUI: () => void}) {
|
|
190
230
|
const toast = useToast();
|
|
191
231
|
return (
|
|
192
232
|
<Button
|
|
193
233
|
onClick={() => {
|
|
234
|
+
showUI();
|
|
194
235
|
if (!pswp?.currSlide?.data.src) {
|
|
195
236
|
toast.error({title: "Unable to copy."});
|
|
196
237
|
return;
|
|
@@ -208,7 +249,7 @@ function CopyURLButton({pswp}: {pswp: PhotoSwipe}) {
|
|
|
208
249
|
}
|
|
209
250
|
}}
|
|
210
251
|
size="medium"
|
|
211
|
-
variant="
|
|
252
|
+
variant="soft"
|
|
212
253
|
color="neutral"
|
|
213
254
|
iconStart={<Link />}
|
|
214
255
|
type="button"
|
package/src/kbd.tsx
CHANGED
|
@@ -14,7 +14,7 @@ export const kbdCss = css`
|
|
|
14
14
|
height: ${space.s18}px;
|
|
15
15
|
padding: ${space.s2}px;
|
|
16
16
|
border-radius: ${border.radius4}px;
|
|
17
|
-
border: ${themeVars.
|
|
17
|
+
border: ${themeVars.shortcutBorder};
|
|
18
18
|
color: ${themeVars.shortcutTextColor};
|
|
19
19
|
line-height: ${lineHeight.nowrap};
|
|
20
20
|
white-space: nowrap;
|
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,
|
package/src/tooltip.tsx
CHANGED
|
@@ -111,7 +111,7 @@ export const TooltipProvider: React.FC<TooltipPrimitive.TooltipProviderProps> =
|
|
|
111
111
|
<TooltipPrimitive.Provider disableHoverableContent skipDelayDuration={150} {...props} />
|
|
112
112
|
);
|
|
113
113
|
|
|
114
|
-
export type TooltipProps = Omit<TooltipPrimitive.TooltipTriggerProps, "title"> & {
|
|
114
|
+
export type TooltipProps = Omit<TooltipPrimitive.TooltipTriggerProps, "title" | "content"> & {
|
|
115
115
|
title?: ReactNode;
|
|
116
116
|
description?: ReactNode;
|
|
117
117
|
tooltipContentStyle?: string;
|
package/src/use-is-phone.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import {ReactNode, useLayoutEffect} from "react";
|
|
|
2
2
|
import {createContext} from "@fibery/react/src/create-context";
|
|
3
3
|
import {useIsMediaQueryMatched} from "./use-is-media-query-matched";
|
|
4
4
|
import {getMediaQueryList} from "./media-query-utils";
|
|
5
|
+
import {mobileRootClassName} from "./mobile-styles";
|
|
5
6
|
|
|
6
7
|
const [Provider, useIsPhone] = createContext<boolean>("PhoneContext", false);
|
|
7
8
|
|
|
@@ -23,36 +24,43 @@ function isVerticalMobileBreakpointDisabled() {
|
|
|
23
24
|
return urlParams.has("no-vertical-mobile-breakpoint");
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
export const isPhoneApp =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export const isPhoneApp = () =>
|
|
28
|
+
Boolean(
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
30
|
+
// @ts-ignore - injected by consumer app
|
|
31
|
+
window.__FIBERY_MOBILE_APP_API
|
|
32
|
+
);
|
|
31
33
|
|
|
32
34
|
export const phoneMediaQuery = isVerticalMobileBreakpointDisabled()
|
|
33
35
|
? "(max-width: 450px)"
|
|
34
36
|
: "(max-width: 450px), (max-height: 450px)";
|
|
35
37
|
|
|
36
38
|
export function isPhoneMode() {
|
|
37
|
-
return isPhoneApp || getMediaQueryList(phoneMediaQuery).matches;
|
|
39
|
+
return isPhoneApp() || getMediaQueryList(phoneMediaQuery).matches;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export function IsPhoneProvider({children}: {children: ReactNode}) {
|
|
41
43
|
const isPhone = useIsMediaQueryMatched(phoneMediaQuery);
|
|
42
44
|
useLayoutEffect(() => {
|
|
43
45
|
const htmlElement = document.querySelector("html");
|
|
44
|
-
if (!htmlElement || isPhoneApp) {
|
|
46
|
+
if (!htmlElement || isPhoneApp()) {
|
|
47
|
+
if (htmlElement) {
|
|
48
|
+
htmlElement.classList.add(mobileRootClassName);
|
|
49
|
+
return () => {
|
|
50
|
+
htmlElement.classList.remove(mobileRootClassName);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// eslint-disable-next-line consistent-return
|
|
45
54
|
return;
|
|
46
55
|
}
|
|
47
56
|
if (isPhone) {
|
|
48
|
-
htmlElement.classList.add(
|
|
57
|
+
htmlElement.classList.add(mobileRootClassName);
|
|
49
58
|
} else {
|
|
50
|
-
htmlElement.classList.remove(
|
|
59
|
+
htmlElement.classList.remove(mobileRootClassName);
|
|
51
60
|
}
|
|
52
|
-
// eslint-disable-next-line consistent-return
|
|
53
61
|
return () => {
|
|
54
|
-
htmlElement.classList.remove(
|
|
62
|
+
htmlElement.classList.remove(mobileRootClassName);
|
|
55
63
|
};
|
|
56
64
|
}, [isPhone]);
|
|
57
|
-
return <Provider value={isPhoneApp || isPhone}>{children}</Provider>;
|
|
65
|
+
return <Provider value={isPhoneApp() || isPhone}>{children}</Provider>;
|
|
58
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ReactNode, useEffect, useState} from "react";
|
|
2
2
|
import {createContext} from "@fibery/react/src/create-context";
|
|
3
3
|
import _ from "lodash";
|
|
4
|
-
import {
|
|
4
|
+
import {isPhoneApp} from "./use-is-phone";
|
|
5
5
|
import {keyboardHeightVar, mobileKeyboardOpenClassName} from "./mobile-styles";
|
|
6
6
|
|
|
7
7
|
const defaultKeyboardHeight = 350;
|
|
@@ -17,13 +17,12 @@ const [IsOnScreenKeyboardOpenedValueProvider, useIsOnScreenKeyboardOpened] = cre
|
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
export function OnScreenKeyboardContextProvider({children}: {children: ReactNode}) {
|
|
20
|
-
const isPhone = useIsPhone();
|
|
21
20
|
const [keyboardHeight, setKeyboardHeight] = useState<number>(defaultKeyboardHeight);
|
|
22
21
|
const [keyboardVisible, setKeyboardVisible] = useState<boolean>(false);
|
|
23
22
|
const [initialViewPortHeight] = useState(() => window.visualViewport?.height || 0);
|
|
24
23
|
useEffect(() => {
|
|
25
24
|
const viewport = window.visualViewport;
|
|
26
|
-
if (!viewport || !
|
|
25
|
+
if (!viewport || !isPhoneApp()) {
|
|
27
26
|
return _.noop;
|
|
28
27
|
}
|
|
29
28
|
|
|
@@ -55,7 +54,7 @@ export function OnScreenKeyboardContextProvider({children}: {children: ReactNode
|
|
|
55
54
|
return () => {
|
|
56
55
|
viewport.removeEventListener("resize", viewportHandler);
|
|
57
56
|
};
|
|
58
|
-
}, [initialViewPortHeight,
|
|
57
|
+
}, [initialViewPortHeight, keyboardHeight, keyboardVisible]);
|
|
59
58
|
return (
|
|
60
59
|
<OnScreenKeyboardHeightValueProvider value={keyboardHeight}>
|
|
61
60
|
<IsOnScreenKeyboardOpenedValueProvider value={keyboardVisible}>{children}</IsOnScreenKeyboardOpenedValueProvider>
|