@fibery/ui-kit 1.38.2 → 1.39.1

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @fibery/ui-kit
2
2
 
3
+ ## 1.39.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 66dca07: add mode attribute for code block node
8
+
9
+ ## 1.39.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 004d205: make prosemirror code block node resizable
14
+
3
15
  ## 1.38.2
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.38.2",
3
+ "version": "1.39.1",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "dependencies": {
@@ -283,6 +283,8 @@ type Props = {
283
283
  after?: ReactNode;
284
284
  floating?: ReactNode;
285
285
  cover?: ReactNode;
286
+ // TODO: refactor. Extract Cover container
287
+ autoCoverHeight?: boolean;
286
288
  size: CardSize;
287
289
  contentStyle?: CSSProperties;
288
290
  backgroundColors?: string[] | null;
@@ -298,6 +300,7 @@ const cardContent = css`
298
300
 
299
301
  export const CardContainer = ({
300
302
  cover,
303
+ autoCoverHeight,
301
304
  before,
302
305
  title,
303
306
  after,
@@ -333,7 +336,7 @@ export const CardContainer = ({
333
336
  {showCover && (
334
337
  <div
335
338
  className={cx(coverClassName, hasContent && coverWithContentClassName)}
336
- style={{height: coverHeightBySize[size] || 0}}
339
+ style={!autoCoverHeight ? {height: coverHeightBySize[size] || 0} : undefined}
337
340
  >
338
341
  {cover}
339
342
  </div>
@@ -55,6 +55,7 @@ export const CollapsibleSection = ({
55
55
  headerCss,
56
56
  css`
57
57
  cursor: pointer;
58
+ user-select: none;
58
59
  `
59
60
  )}
60
61
  onClick={onToggle}
@@ -21,6 +21,7 @@ import {copyToClipboard} from "./copy-to-clipboard";
21
21
  import {useToast} from "./toast/toast-provider";
22
22
  import {Icon} from "./icons/Icon";
23
23
  import {useImagesGalleryMethods, useImagesGalleryRegistry} from "./images-gallery/images-gallery";
24
+ import {isPhoneApp} from "./use-is-phone";
24
25
 
25
26
  const fileItemStyle = css`
26
27
  display: block;
@@ -79,12 +80,14 @@ export type FileItemType = {
79
80
 
80
81
  export type FileItemProps = {
81
82
  data: FileItemType;
83
+ onDownloadClick?: () => void;
82
84
  onRemoveClick?: (x: FileItemType) => void;
83
85
  disabled?: boolean;
84
86
  };
85
87
 
86
88
  export function FileItem(props: FileItemProps) {
87
- const {name, url, thumbnailUrl, status, previewData: preview, contentType} = props.data;
89
+ const {onDownloadClick, onRemoveClick} = props;
90
+ const {name, thumbnailUrl, status, previewData: preview, contentType} = props.data;
88
91
  const isPending = status === "uploading" || status === "deleting";
89
92
  const isBackgroundReady = Boolean(thumbnailUrl && !isPending);
90
93
  const [isActionsOpened, setIsActionsOpened] = useState(false);
@@ -105,9 +108,9 @@ export function FileItem(props: FileItemProps) {
105
108
  src: preview.originalSrc,
106
109
  zoomed: false,
107
110
  setZoomed,
108
- actions: <FilePreviewActions url={preview.originalSrc} name={name} />,
111
+ actions: <FilePreviewActions url={preview.originalSrc} name={name} onDownloadClick={onDownloadClick} />,
109
112
  });
110
- }, [imagesRegistry, name, preview]);
113
+ }, [imagesRegistry, name, preview, onDownloadClick]);
111
114
 
112
115
  useEffect(() => {
113
116
  if (!preview) {
@@ -126,18 +129,18 @@ export function FileItem(props: FileItemProps) {
126
129
  src: preview.originalSrc,
127
130
  zoomed,
128
131
  setZoomed,
129
- actions: <FilePreviewActions url={preview.originalSrc} name={name} />,
132
+ actions: <FilePreviewActions url={preview.originalSrc} name={name} onDownloadClick={onDownloadClick} />,
130
133
  });
131
134
  }
132
- }, [galleryMethods, zoomed, preview, imagesRegistry, name]);
135
+ }, [galleryMethods, zoomed, preview, imagesRegistry, name, onDownloadClick]);
133
136
 
134
137
  const onContainerClick = useCallback(() => {
135
138
  if (preview) {
136
139
  setZoomed(true);
137
140
  } else {
138
- stratFileDownload(url, name);
141
+ onDownloadClick?.();
139
142
  }
140
- }, [name, preview, url]);
143
+ }, [preview, onDownloadClick]);
141
144
 
142
145
  return (
143
146
  <div
@@ -248,7 +251,8 @@ export function FileItem(props: FileItemProps) {
248
251
  data={props.data}
249
252
  disabled={props.disabled}
250
253
  isBackgroundReady={isBackgroundReady}
251
- onRemoveClick={props.onRemoveClick}
254
+ onDownloadClick={onDownloadClick}
255
+ onRemoveClick={onRemoveClick}
252
256
  />
253
257
  </div>
254
258
  </div>
@@ -278,6 +282,7 @@ export function FileItem(props: FileItemProps) {
278
282
  data={props.data}
279
283
  disabled={props.disabled}
280
284
  isBackgroundReady={isBackgroundReady}
285
+ onDownloadClick={onDownloadClick}
281
286
  onRemoveClick={props.onRemoveClick}
282
287
  />
283
288
  </div>
@@ -305,10 +310,12 @@ function FileActionsMenu({
305
310
  data,
306
311
  disabled,
307
312
  setIsActionsOpened,
313
+ onDownloadClick,
308
314
  onRemoveClick,
309
315
  isBackgroundReady,
310
316
  }: {
311
317
  data: FileItemType;
318
+ onDownloadClick?: () => void;
312
319
  onRemoveClick?: (data: FileItemType) => void;
313
320
  disabled?: boolean;
314
321
  setIsActionsOpened: (val: boolean) => void;
@@ -339,11 +346,19 @@ function FileActionsMenu({
339
346
  Copy file URL
340
347
  </ActionsMenuItem>
341
348
  <a href={data.url} target="_blank" rel="noopener noreferrer">
342
- <ActionsMenuItem Icon={RicheditorOpenLink}>Open in new tab</ActionsMenuItem>
343
- </a>
344
- <a download={data.name} href={`${data.url}?attachment`}>
345
- <ActionsMenuItem Icon={FileDownload}>Download</ActionsMenuItem>
349
+ <ActionsMenuItem Icon={RicheditorOpenLink}>
350
+ {isPhoneApp() ? "Open in browser" : "Open in new tab"}
351
+ </ActionsMenuItem>
346
352
  </a>
353
+ {isPhoneApp() ? (
354
+ <ActionsMenuItem Icon={FileDownload} onSelect={onDownloadClick}>
355
+ Download
356
+ </ActionsMenuItem>
357
+ ) : (
358
+ <a download={data.name} href={`${data.url}?attachment`}>
359
+ <ActionsMenuItem Icon={FileDownload}>Download</ActionsMenuItem>
360
+ </a>
361
+ )}
347
362
  <ActionsMenuSeparator />
348
363
  <ActionsMenuItem Icon={Delete} onSelect={() => onRemoveClick?.(data)} dangerous disabled={disabled}>
349
364
  Delete
@@ -387,7 +402,20 @@ function FileTitle({title, className}: {title: string; className?: string}) {
387
402
  );
388
403
  }
389
404
 
390
- function FilePreviewActions({url, name}: {url: string; name: string}) {
405
+ function FilePreviewActions({url, name, onDownloadClick}: {url: string; name: string; onDownloadClick?: () => void}) {
406
+ if (isPhoneApp()) {
407
+ return (
408
+ <>
409
+ <a href={url} target="_blank" rel="noopener noreferrer">
410
+ <ActionsMenuItem Icon={RicheditorOpenLink}>Open in browser</ActionsMenuItem>
411
+ </a>
412
+ <ActionsMenuItem Icon={FileDownload} onSelect={onDownloadClick}>
413
+ Download
414
+ </ActionsMenuItem>
415
+ </>
416
+ );
417
+ }
418
+
391
419
  return (
392
420
  <>
393
421
  <a href={url} target="_blank" rel="noopener noreferrer">
@@ -400,17 +428,6 @@ function FilePreviewActions({url, name}: {url: string; name: string}) {
400
428
  );
401
429
  }
402
430
 
403
- function stratFileDownload(url: string, filename: string) {
404
- const a = document.createElement("a");
405
- a.href = `${url}?attachment`;
406
- a.download = filename;
407
- a.style.display = "none";
408
-
409
- document.body.appendChild(a);
410
- a.click();
411
- document.body.removeChild(a);
412
- }
413
-
414
431
  function FileIcon({contentType}: {contentType: string}) {
415
432
  if (contentType.startsWith("image/")) {
416
433
  return <FileImage />;
package/src/file-item.tsx CHANGED
@@ -13,6 +13,7 @@ import Link from "./icons/react/Link";
13
13
  import Pencil from "./icons/react/Pencil";
14
14
  import {copyToClipboard} from "./copy-to-clipboard";
15
15
  import {useToast} from "./toast/toast-provider";
16
+ import {isPhoneApp} from "./use-is-phone";
16
17
 
17
18
  const fileItemStyle = css`
18
19
  ${{
@@ -86,12 +87,14 @@ export type FileItemType = {
86
87
 
87
88
  export type FileItemProps = {
88
89
  data: FileItemType;
90
+ onDownloadClick?: () => void;
89
91
  onRemoveClick?: (x: FileItemType) => void;
90
92
  disabled?: boolean;
91
93
  onRenameClick?: (p: {id: string; name: string}) => void;
92
94
  };
93
95
 
94
96
  export function FileItem(props: FileItemProps) {
97
+ const {onDownloadClick, onRemoveClick} = props;
95
98
  const {name, url, thumbnailUrl, status, previewData: preview} = props.data;
96
99
  const isActive = status === "uploading" || status === "deleting";
97
100
  const isBackgroundReady = thumbnailUrl && !isActive;
@@ -111,12 +114,12 @@ export function FileItem(props: FileItemProps) {
111
114
  contentType: preview.contentType,
112
115
  zoomed: false,
113
116
  setZoomed,
114
- actions: <FilePreviewActions url={preview.originalSrc} name={name} />,
117
+ actions: <FilePreviewActions url={preview.originalSrc} name={name} onDownloadClick={onDownloadClick} />,
115
118
  });
116
119
  return () => {
117
120
  imagesRegistry.delete(preview.originalSrc);
118
121
  };
119
- }, [imagesRegistry, name, preview]);
122
+ }, [imagesRegistry, name, preview, onDownloadClick]);
120
123
 
121
124
  useEffect(() => {
122
125
  if (!preview) {
@@ -136,25 +139,18 @@ export function FileItem(props: FileItemProps) {
136
139
  contentType: preview.contentType,
137
140
  zoomed,
138
141
  setZoomed,
139
- actions: <FilePreviewActions url={preview.originalSrc} name={name} />,
142
+ actions: <FilePreviewActions url={preview.originalSrc} name={name} onDownloadClick={onDownloadClick} />,
140
143
  });
141
144
  }
142
- }, [galleryMethods, zoomed, preview, imagesRegistry, name]);
145
+ }, [galleryMethods, zoomed, preview, imagesRegistry, name, onDownloadClick]);
143
146
 
144
147
  const onContainerClick = useCallback(() => {
145
148
  if (preview) {
146
149
  setZoomed(true);
147
150
  } else {
148
- const a = document.createElement("a");
149
- a.href = `${url}?attachment`;
150
- a.download = name;
151
- a.style.display = "none";
152
-
153
- document.body.appendChild(a);
154
- a.click();
155
- document.body.removeChild(a);
151
+ onDownloadClick?.();
156
152
  }
157
- }, [name, preview, url]);
153
+ }, [preview, onDownloadClick]);
158
154
 
159
155
  return (
160
156
  <div
@@ -211,9 +207,15 @@ export function FileItem(props: FileItemProps) {
211
207
  <a href={url} target="_blank" rel="noopener noreferrer">
212
208
  <ActionsMenuItem Icon={RicheditorOpenLink}>Open in new tab</ActionsMenuItem>
213
209
  </a>
214
- <a download={name} href={props.data.downloadUrl ?? url}>
215
- <ActionsMenuItem Icon={FileDownload}>Download</ActionsMenuItem>
216
- </a>
210
+ {isPhoneApp() ? (
211
+ <ActionsMenuItem Icon={FileDownload} onSelect={onDownloadClick}>
212
+ Download
213
+ </ActionsMenuItem>
214
+ ) : (
215
+ <a download={name} href={props.data.downloadUrl ?? url}>
216
+ <ActionsMenuItem Icon={FileDownload}>Download</ActionsMenuItem>
217
+ </a>
218
+ )}
217
219
  {props.onRenameClick && status === "done" ? (
218
220
  <ActionsMenuItem
219
221
  Icon={Pencil}
@@ -224,7 +226,7 @@ export function FileItem(props: FileItemProps) {
224
226
  ) : null}
225
227
  <ActionsMenuItem
226
228
  Icon={Delete}
227
- onSelect={() => props.onRemoveClick?.(props.data)}
229
+ onSelect={() => onRemoveClick?.(props.data)}
228
230
  dangerous
229
231
  disabled={props.disabled}
230
232
  >
@@ -288,7 +290,20 @@ function resolveStatusText(status?: string) {
288
290
  return statusText;
289
291
  }
290
292
 
291
- function FilePreviewActions({url, name}: {url: string; name: string}) {
293
+ function FilePreviewActions({url, name, onDownloadClick}: {url: string; name: string; onDownloadClick?: () => void}) {
294
+ if (isPhoneApp()) {
295
+ return (
296
+ <>
297
+ <a href={url} target="_blank" rel="noopener noreferrer">
298
+ <ActionsMenuItem Icon={RicheditorOpenLink}>Open in browser</ActionsMenuItem>
299
+ </a>
300
+ <ActionsMenuItem Icon={FileDownload} onSelect={onDownloadClick}>
301
+ Download
302
+ </ActionsMenuItem>
303
+ </>
304
+ );
305
+ }
306
+
292
307
  return (
293
308
  <>
294
309
  <a href={url} target="_blank" rel="noopener noreferrer">
@@ -24,25 +24,26 @@ function isVerticalMobileBreakpointDisabled() {
24
24
  return urlParams.has("no-vertical-mobile-breakpoint");
25
25
  }
26
26
 
27
- export const isPhoneApp = Boolean(
28
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
29
- // @ts-ignore - injected by consumer app
30
- window.__FIBERY_MOBILE_APP_API
31
- );
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
+ );
32
33
 
33
34
  export const phoneMediaQuery = isVerticalMobileBreakpointDisabled()
34
35
  ? "(max-width: 450px)"
35
36
  : "(max-width: 450px), (max-height: 450px)";
36
37
 
37
38
  export function isPhoneMode() {
38
- return isPhoneApp || getMediaQueryList(phoneMediaQuery).matches;
39
+ return isPhoneApp() || getMediaQueryList(phoneMediaQuery).matches;
39
40
  }
40
41
 
41
42
  export function IsPhoneProvider({children}: {children: ReactNode}) {
42
43
  const isPhone = useIsMediaQueryMatched(phoneMediaQuery);
43
44
  useLayoutEffect(() => {
44
45
  const htmlElement = document.querySelector("html");
45
- if (!htmlElement || isPhoneApp) {
46
+ if (!htmlElement || isPhoneApp()) {
46
47
  if (htmlElement) {
47
48
  htmlElement.classList.add(mobileRootClassName);
48
49
  return () => {
@@ -61,5 +62,5 @@ export function IsPhoneProvider({children}: {children: ReactNode}) {
61
62
  htmlElement.classList.remove(mobileRootClassName);
62
63
  };
63
64
  }, [isPhone]);
64
- return <Provider value={isPhoneApp || isPhone}>{children}</Provider>;
65
+ return <Provider value={isPhoneApp() || isPhone}>{children}</Provider>;
65
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 {useIsPhone} from "./use-is-phone";
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 || !isPhone) {
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, isPhone, keyboardHeight, keyboardVisible]);
57
+ }, [initialViewPortHeight, keyboardHeight, keyboardVisible]);
59
58
  return (
60
59
  <OnScreenKeyboardHeightValueProvider value={keyboardHeight}>
61
60
  <IsOnScreenKeyboardOpenedValueProvider value={keyboardVisible}>{children}</IsOnScreenKeyboardOpenedValueProvider>