@edifice.io/react 2.5.15-develop-pedago.20260330150114 → 2.5.15-develop-enabling.20260401143546

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.
Files changed (50) hide show
  1. package/dist/components/Card/Card.js +1 -1
  2. package/dist/components/Combobox/Combobox.js +1 -1
  3. package/dist/components/Dropdown/Dropdown.js +1 -1
  4. package/dist/components/Form/FormControl.js +1 -1
  5. package/dist/components/Layout/components/WidgetApps.js +1 -1
  6. package/dist/components/Modal/Modal.js +1 -1
  7. package/dist/components/PreventPropagation/PreventPropagation.js +1 -1
  8. package/dist/components/PromotionCard/PromotionCard.js +1 -1
  9. package/dist/components/SearchBar/SearchBar.d.ts +2 -2
  10. package/dist/components/Table/components/Table.js +1 -1
  11. package/dist/hooks/useConf/useConf.d.ts +1 -1
  12. package/dist/hooks/useDropdown/useDropdown.js +2 -0
  13. package/dist/hooks/useSession/useSession.d.ts +1 -1
  14. package/dist/hooks/useThumbnail/useThumbnail.d.ts +1 -1
  15. package/dist/hooks/useThumbnail/useThumbnail.js +5 -1
  16. package/dist/hooks/useUpload/useUpload.js +2 -5
  17. package/dist/hooks/useWorkspaceFolders/useWorkspaceFolders.d.ts +12 -0
  18. package/dist/hooks/useWorkspaceFolders/useWorkspaceFolders.js +1 -1
  19. package/dist/modules/editor/components/Editor/EditorPreview.js +1 -1
  20. package/dist/modules/modals/ResourceModal/ResourceModal.d.ts +1 -1
  21. package/dist/modules/modals/ResourceModal/ResourceModal.js +1 -1
  22. package/dist/modules/modals/ResourceModal/hooks/useUpdateMutation.d.ts +1 -1
  23. package/dist/modules/modals/ShareModal/ShareResources.d.ts +4 -53
  24. package/dist/modules/modals/ShareModal/apps/ShareBlog.d.ts +1 -1
  25. package/dist/modules/modals/ShareModal/hooks/useShareBookmark.js +2 -1
  26. package/dist/modules/modals/ShareModal/hooks/useShareMutation.d.ts +1 -1
  27. package/dist/modules/multimedia/ImageEditor/components/ImageEditor.js +24 -10
  28. package/dist/modules/multimedia/ImageEditor/effects/blur.d.ts +7 -5
  29. package/dist/modules/multimedia/ImageEditor/effects/blur.js +26 -18
  30. package/dist/modules/multimedia/ImageEditor/effects/constants.d.ts +2 -0
  31. package/dist/modules/multimedia/ImageEditor/effects/constants.js +4 -0
  32. package/dist/modules/multimedia/ImageEditor/effects/crop.d.ts +4 -5
  33. package/dist/modules/multimedia/ImageEditor/effects/crop.js +42 -26
  34. package/dist/modules/multimedia/ImageEditor/effects/misc.d.ts +10 -16
  35. package/dist/modules/multimedia/ImageEditor/effects/misc.js +27 -12
  36. package/dist/modules/multimedia/ImageEditor/effects/resize.d.ts +3 -7
  37. package/dist/modules/multimedia/ImageEditor/effects/resize.js +31 -22
  38. package/dist/modules/multimedia/ImageEditor/effects/rotate.js +2 -3
  39. package/dist/modules/multimedia/ImageEditor/hooks/useHistoryTool.js +40 -41
  40. package/dist/modules/multimedia/ImageEditor/hooks/useImageEditor.d.ts +1 -1
  41. package/dist/modules/multimedia/ImageEditor/hooks/useImageEditor.js +9 -3
  42. package/dist/modules/multimedia/ImageEditor/hooks/useImageEffects.js +4 -1
  43. package/dist/modules/multimedia/LinkerCard/LinkerCard._.d.ts +1 -1
  44. package/dist/modules/multimedia/MediaLibrary/MediaLibrary.js +1 -0
  45. package/dist/modules/multimedia/UploadCard/UploadCard._.d.ts +1 -1
  46. package/dist/modules/multimedia/UploadFiles/UploadFiles.js +4 -4
  47. package/dist/providers/EdificeClientProvider/EdificeClientProvider.context.d.ts +1 -1
  48. package/dist/utilities/react-query/react-query-utils.d.ts +1 -1
  49. package/package.json +52 -53
  50. package/dist/modules/multimedia/FileCard/FileCard._.d.ts +0 -17
@@ -39,7 +39,7 @@ const Root = /* @__PURE__ */ forwardRef(({
39
39
  /* @__PURE__ */ jsx(Card.Header, {}),
40
40
  typeof children == "function" ? children(appCode) : children
41
41
  ] }) });
42
- }), Card = /* @__PURE__ */ Object.assign(Root, {
42
+ }), Card = Object.assign(Root, {
43
43
  Title: CardTitle,
44
44
  Text: CardText,
45
45
  Image: CardImage,
@@ -53,7 +53,7 @@ const ComboboxComponent = /* @__PURE__ */ forwardRef(({
53
53
  }, value, variant, renderInputGroup, renderSelectedItems, hasDefault: !!options.length, onFocus, onBlur, inputRef }),
54
54
  /* @__PURE__ */ jsx(Dropdown.Menu, { "data-testid": "combobox-search-menu", children: renderContent() })
55
55
  ] });
56
- }), Combobox = /* @__PURE__ */ Object.assign(ComboboxComponent, {
56
+ }), Combobox = Object.assign(ComboboxComponent, {
57
57
  Trigger: ComboboxTrigger
58
58
  });
59
59
  export {
@@ -67,7 +67,7 @@ const Root = /* @__PURE__ */ forwardRef(({
67
67
  return useEffect(() => {
68
68
  onToggle == null || onToggle(visible);
69
69
  }, [visible]), /* @__PURE__ */ jsx(DropdownContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { ref, className: dropdown, children: typeof children == "function" ? children(triggerProps, itemRefs, setVisible) : children }) });
70
- }), Dropdown = /* @__PURE__ */ Object.assign(Root, {
70
+ }), Dropdown = Object.assign(Root, {
71
71
  Trigger: DropdownTrigger,
72
72
  Menu: DropdownMenu,
73
73
  Item: DropdownItem,
@@ -22,7 +22,7 @@ const Root = /* @__PURE__ */ forwardRef(({
22
22
  status
23
23
  }), [id, isOptional, isReadOnly, isRequired, status]);
24
24
  return /* @__PURE__ */ jsx(Context.Provider, { value: values, children: /* @__PURE__ */ jsx("div", { ref, className, ...restProps, children }) });
25
- }), FormControl = /* @__PURE__ */ Object.assign(Root, {
25
+ }), FormControl = Object.assign(Root, {
26
26
  Label,
27
27
  Input,
28
28
  Text: FormText
@@ -14,7 +14,7 @@ const WidgetAppsFooter = () => {
14
14
  } = useTranslation(), getAppName = (data) => data.prefix && data.prefix.length > 1 ? t(data.prefix.substring(1)) : t(data.displayName) || "";
15
15
  return /* @__PURE__ */ jsxs("div", { className: "widget-body d-flex flex-wrap", children: [
16
16
  !bookmarkedApps.length && /* @__PURE__ */ jsx("div", { className: "text-dark", children: t("navbar.myapps.more") }),
17
- bookmarkedApps.slice(0, 6).map((app, index) => /* @__PURE__ */ jsx("a", { href: app.address, title: getAppName(app), className: "bookmarked-app", target: appToOpenOnBlank.includes(app.name) || app.isExternal || app.category === "connector" ? "_blank" : void 0, rel: appToOpenOnBlank.includes(app.name) || app.isExternal || app.category === "connector" ? "noopener noreferrer" : void 0, children: /* @__PURE__ */ jsx(AppIcon, { app, size: "32" }) }, index))
17
+ bookmarkedApps.slice(0, 6).map((app, index) => /* @__PURE__ */ jsx("a", { href: app.address, title: getAppName(app), className: "bookmarked-app", target: appToOpenOnBlank.includes(app.name) || app.isExternal || app.target === "_blank" ? "_blank" : void 0, rel: appToOpenOnBlank.includes(app.name) || app.isExternal || app.target === "_blank" ? "noopener noreferrer" : void 0, children: /* @__PURE__ */ jsx(AppIcon, { app, size: "32" }) }, index))
18
18
  ] });
19
19
  };
20
20
  export {
@@ -62,7 +62,7 @@ const Root = /* @__PURE__ */ forwardRef(({
62
62
  opacity: 0.65
63
63
  } })
64
64
  ] })) });
65
- }), Modal = /* @__PURE__ */ Object.assign(Root, {
65
+ }), Modal = Object.assign(Root, {
66
66
  Header: ModalHeader,
67
67
  Subtitle: ModalSubtitle,
68
68
  Body: ModalBody,
@@ -3,7 +3,7 @@ const Root = ({
3
3
  children
4
4
  }) => /* @__PURE__ */ jsx("div", { onClick: (e) => {
5
5
  e.stopPropagation();
6
- }, children }), PreventPropagation = /* @__PURE__ */ Object.assign(Root, {});
6
+ }, children }), PreventPropagation = Object.assign(Root, {});
7
7
  export {
8
8
  PreventPropagation as default
9
9
  };
@@ -17,7 +17,7 @@ const Root = ({
17
17
  borderColor,
18
18
  backgroundColor
19
19
  }, children });
20
- }, PromotionCard = /* @__PURE__ */ Object.assign(Root, {
20
+ }, PromotionCard = Object.assign(Root, {
21
21
  Header: PromotionCardHeader,
22
22
  Body: PromotionCardBody,
23
23
  Icon: PromotionCardIcon,
@@ -3,7 +3,7 @@ import { Size } from '../../types';
3
3
  /**
4
4
  * Base props shared by both SearchBar variants
5
5
  */
6
- export interface BaseProps extends Omit<React.ComponentPropsWithoutRef<'input'>, 'size'> {
6
+ interface BaseProps extends Omit<React.ComponentPropsWithoutRef<'input'>, 'size'> {
7
7
  /**
8
8
  * String or template literal key for i18next translation
9
9
  */
@@ -67,7 +67,7 @@ type DynamicSearchBar = {
67
67
  /**
68
68
  * Props for the SearchBar component
69
69
  */
70
- export type Props = DefaultSearchBar | DynamicSearchBar;
70
+ type Props = DefaultSearchBar | DynamicSearchBar;
71
71
  export type SearchBarProps = BaseProps & Props;
72
72
  /**
73
73
  * SearchBar component to handle dynamic or static search input
@@ -13,7 +13,7 @@ const Root = /* @__PURE__ */ forwardRef(({
13
13
  overflowY: "auto"
14
14
  } : {}, children: /* @__PURE__ */ jsx("table", { ref, className: "table align-middle mb-0", style: {
15
15
  overflow: maxHeight ? "visible" : "hidden"
16
- }, children }) })), Table = /* @__PURE__ */ Object.assign(Root, {
16
+ }, children }) })), Table = Object.assign(Root, {
17
17
  Thead: TableThead,
18
18
  Th: TableTh,
19
19
  Tbody: TableTbody,
@@ -1,4 +1,4 @@
1
1
  import { App, IGetConf } from '@edifice.io/client';
2
2
  export default function useConf({ appCode }: {
3
3
  appCode: App;
4
- }): import('../../../node_modules/@tanstack/react-query').UseQueryResult<IGetConf, Error>;
4
+ }): import('@tanstack/react-query').UseQueryResult<IGetConf, Error>;
@@ -73,6 +73,8 @@ const useDropdown = (placement, extraTriggerKeyDownHandler, isTriggerHovered = !
73
73
  case "Space":
74
74
  if (!openOnSpace)
75
75
  break;
76
+ openDropdown(), flag = !0;
77
+ break;
76
78
  case "Enter":
77
79
  case "ArrowDown":
78
80
  case "Down":
@@ -1,2 +1,2 @@
1
1
  import { IGetSession } from '@edifice.io/client';
2
- export default function useSession(): import('../../../node_modules/@tanstack/react-query').UseQueryResult<IGetSession, Error>;
2
+ export default function useSession(): import('@tanstack/react-query').UseQueryResult<IGetSession, Error>;
@@ -10,5 +10,5 @@ type LazyLoadOptions = {
10
10
  * with respect to the [`intersectionOptions`](https://github.com/thebuilder/react-intersection-observer/tree/dceba7f52aebea4d62d539bc55a1db129226bb6c?tab=readme-ov-file#options)
11
11
  * @return `true` if `src` is available, or false otherwise, or `null` while not tested.
12
12
  */
13
- export default function useThumbnail(src: string, options?: LazyLoadOptions): boolean | null;
13
+ export default function useThumbnail(src: string | null | undefined, options?: LazyLoadOptions): boolean | null;
14
14
  export {};
@@ -13,6 +13,10 @@ function useThumbnail(src, options) {
13
13
  (_a = options == null ? void 0 : options.ref) != null && _a.current && (inViewRef == null || inViewRef(options.ref.current));
14
14
  }, [options == null ? void 0 : options.ref, inViewRef]), useEffect(() => {
15
15
  if ((options == null ? void 0 : options.ref) === void 0 || inView) {
16
+ if (!src) {
17
+ setStatus(!1);
18
+ return;
19
+ }
16
20
  const img = new Image();
17
21
  return img.src = src, img.onload = () => {
18
22
  setStatus(!0);
@@ -22,7 +26,7 @@ function useThumbnail(src, options) {
22
26
  img.onload = null, img.onerror = null, setStatus(null);
23
27
  };
24
28
  }
25
- }, [inView]), status;
29
+ }, [inView, src]), status;
26
30
  }
27
31
  export {
28
32
  useThumbnail as default
@@ -87,11 +87,8 @@ const useUpload = (visibility, application = "media-library") => {
87
87
  children: [],
88
88
  created: /* @__PURE__ */ new Date(),
89
89
  _shared: [],
90
- _isShared: !1,
91
- owner: {
92
- userId: "",
93
- displayName: ""
94
- }
90
+ isShared: !1,
91
+ owner: ""
95
92
  };
96
93
  throw uploadResponse.state === "error" ? new Error(uploadResponse.code || "Error while uploading video") : new Error("Video encoding is still running");
97
94
  };
@@ -0,0 +1,12 @@
1
+ import { WorkspaceElement } from '@edifice.io/client';
2
+ export interface CreateFolderParams {
3
+ folderName: string;
4
+ folderParentId?: string;
5
+ }
6
+ declare function useWorkspaceFolders(): {
7
+ folders: WorkspaceElement[];
8
+ createFolderMutation: import('@tanstack/react-query').UseMutationResult<any, Error, CreateFolderParams, unknown>;
9
+ canCopyFileIntoFolder: (folderId: string) => boolean;
10
+ isLoading: boolean;
11
+ };
12
+ export default useWorkspaceFolders;
@@ -1,8 +1,8 @@
1
1
  import { odeServices } from "@edifice.io/client";
2
2
  import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query";
3
3
  import { useMemo, useCallback } from "react";
4
- import { useEdificeClient } from "../../providers/EdificeClientProvider/EdificeClientProvider.hook.js";
5
4
  import { useTranslation } from "react-i18next";
5
+ import { useEdificeClient } from "../../providers/EdificeClientProvider/EdificeClientProvider.hook.js";
6
6
  import useToast from "../useToast/useToast.js";
7
7
  function useWorkspaceFolders() {
8
8
  const queryClient = useQueryClient(), {
@@ -48,7 +48,7 @@ const EditorPreview = ({
48
48
  }), children: t("editor.preview.moreMedia", {
49
49
  mediaCount: medias.length - (index + 1)
50
50
  }) })
51
- ] }, media.url)) })
51
+ ] }, `${index}-${media.url}`)) })
52
52
  ] });
53
53
  };
54
54
  export {
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { CreateParameters, CreateResult, ID, IFolder, UpdateParameters, UpdateResult } from '@edifice.io/client';
3
- import { UseMutationResult } from '../../../../node_modules/@tanstack/react-query';
3
+ import { UseMutationResult } from '@tanstack/react-query';
4
4
  export interface FormInputs {
5
5
  title: string;
6
6
  description: string;
@@ -157,7 +157,7 @@ const DEFAULT_INPUT_MAX_LENGTH = 60, DEFAULT_TEXTAREA_MAX_LENGTH = 400, Resource
157
157
  /* @__PURE__ */ jsx(Button, { form: formId, type: "submit", color: "primary", isLoading: isSubmitting, variant: "filled", disabled: !isValid || isSubmitting, children: isCreating ? customT.create ?? t("explorer.create") : customT.save ?? t("save") })
158
158
  ] }),
159
159
  /* @__PURE__ */ jsx(MediaLibrary, { appCode: application, ref: mediaLibraryRef, multiple: !1, visibility: "protected", ...mediaLibraryHandlers })
160
- ] }), document.getElementById("portal"));
160
+ ] }), document.getElementById("portal") || document.body);
161
161
  };
162
162
  export {
163
163
  ResourceModal
@@ -1,5 +1,5 @@
1
1
  import { UpdateParameters, UpdateResult } from '@edifice.io/client';
2
- import { UseMutationOptions, UseMutationResult } from '../../../../../node_modules/@tanstack/react-query';
2
+ import { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
3
3
  declare const useUpdateMutation: ({ application, options, }: {
4
4
  application: string;
5
5
  options?: UseMutationOptions<UpdateResult, Error, UpdateParameters>;
@@ -1,5 +1,5 @@
1
1
  import { ID, PutShareResponse, RightStringified, ShareRight, ShareRightAction, ShareRightActionDisplayName, ShareUrls } from '@edifice.io/client';
2
- import { UseMutationResult } from '../../../../node_modules/@tanstack/react-query';
2
+ import { UseMutationResult } from '@tanstack/react-query';
3
3
  /**
4
4
  * Configuration options for sharing a resource
5
5
  *
@@ -118,21 +118,11 @@ interface ShareResourceProps {
118
118
  classNameSearchInput?: string;
119
119
  }
120
120
  /**
121
- * Ref interface exposed by ShareResources component
121
+ * Ref interface exposed by ShareResources component.
122
+ * Use useRef with this type and call ref.current?.handleShare() to trigger share programmatically.
122
123
  *
123
124
  * @interface ShareResourcesRef
124
125
  * @property {() => void} handleShare - Method to trigger the share operation
125
- *
126
- * @example
127
- * ```tsx
128
- * const ref = useRef<ShareResourcesRef>(null);
129
- *
130
- * // Trigger share programmatically
131
- * ref.current?.handleShare();
132
- *
133
- * // Check sharing status
134
- * const sharing = ref.current?.isSharing();
135
- * ```
136
126
  */
137
127
  export interface ShareResourcesRef {
138
128
  handleShare: (notify?: boolean) => void;
@@ -142,46 +132,7 @@ export interface ShareResourcesRef {
142
132
  *
143
133
  * A component for managing resource sharing permissions with users and groups.
144
134
  * Provides search functionality, bookmark management, and granular rights control.
145
- *
146
- * @example
147
- * ```tsx
148
- * import { useRef } from 'react';
149
- * import ShareResources, { ShareResourcesRef, ShareOptions } from './ShareResources';
150
- *
151
- * function MyComponent() {
152
- * const shareRef = useRef<ShareResourcesRef>(null);
153
- *
154
- * const shareOptions: ShareOptions = {
155
- * resourceId: '123',
156
- * resourceRights: [],
157
- * resourceCreatorId: 'user-456',
158
- * filteredActions: ['read', 'contrib'],
159
- * urls: {
160
- * getResourceRights: '/api/share/rights',
161
- * putResourceRights: '/api/share/update'
162
- * }
163
- * };
164
- *
165
- * const handleSave = () => {
166
- * if (shareRef.current) {
167
- * shareRef.current.handleShare();
168
- * }
169
- * };
170
- *
171
- * return (
172
- * <>
173
- * <ShareResources
174
- * ref={shareRef}
175
- * shareOptions={shareOptions}
176
- * onSuccess={() => console.log('Shared successfully')}
177
- * onChange={(rights, isDirty) => console.log('Rights changed:', isDirty)}
178
- * onSubmit={(isSubmitting) => console.log('Submitting share...', isSubmitting)}
179
- * />
180
- * <button onClick={handleSave}>Save Changes</button>
181
- * </>
182
- * );
183
- * }
184
- * ```
135
+ * Use forwardRef to get a ref with handleShare(). See ShareResourcesRef interface.
185
136
  *
186
137
  * @component
187
138
  * @forwardRef
@@ -1,5 +1,5 @@
1
1
  import { ID, UpdateParameters, UpdateResult } from '@edifice.io/client';
2
- import { UseMutationResult } from '../../../../../node_modules/@tanstack/react-query';
2
+ import { UseMutationResult } from '@tanstack/react-query';
3
3
  export type PublicationType = 'RESTRAINT' | 'IMMEDIATE' | undefined;
4
4
  export interface ShareBlogProps {
5
5
  resourceId: ID;
@@ -35,7 +35,8 @@ const useShareBookmark = ({
35
35
  ...shareRights,
36
36
  visibleBookmarks: [...shareRights.visibleBookmarks, {
37
37
  displayName: name,
38
- id: res.id
38
+ id: res.id,
39
+ notVisibleCount: 0
39
40
  }]
40
41
  }
41
42
  }), setBookmark((prev) => ({
@@ -1,5 +1,5 @@
1
1
  import { PutShareResponse, ShareRight } from '@edifice.io/client';
2
- import { UseMutationOptions, UseMutationResult } from '../../../../../node_modules/@tanstack/react-query';
2
+ import { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
3
3
  declare const useShareMutation: ({ application, options, }: {
4
4
  application: string;
5
5
  options?: UseMutationOptions<PutShareResponse, Error, {
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useState } from "react";
3
- import { Stage } from "@pixi/react";
2
+ import { useState, useEffect, useRef, useCallback } from "react";
3
+ import * as PIXI from "pixi.js";
4
4
  import { useTranslation } from "react-i18next";
5
5
  import useImageEditor from "../hooks/useImageEditor.js";
6
6
  import ImageEditorToolbar from "./ImageEditorToolbar.js";
@@ -21,7 +21,11 @@ const ImageEditor = ({
21
21
  }) => {
22
22
  const {
23
23
  t
24
- } = useTranslation(), [currentOperation, setCurrentOperation] = useState(void 0), [isSaving, setSaving] = useState(!1), [altText, setAltText] = useState(altTextParam ?? ""), [legend, setLegend] = useState(legendParam ?? ""), [dirty, setDirty] = useState(!1), {
24
+ } = useTranslation(), [currentOperation, setCurrentOperation] = useState(void 0), [isSaving, setSaving] = useState(!1), [altText, setAltText] = useState(altTextParam ?? ""), [legend, setLegend] = useState(legendParam ?? ""), [dirty, setDirty] = useState(!1);
25
+ useEffect(() => {
26
+ setAltText(altTextParam ?? ""), setLegend(legendParam ?? ""), setDirty(!1);
27
+ }, [altTextParam, legendParam]);
28
+ const {
25
29
  toBlob,
26
30
  setApplication,
27
31
  startBlur,
@@ -36,7 +40,21 @@ const ImageEditor = ({
36
40
  loading
37
41
  } = useImageEditor({
38
42
  imageSrc
39
- }), stopAll = () => {
43
+ }), appRef = useRef(null), containerRef = useCallback((node) => {
44
+ if (node && !appRef.current) {
45
+ const app = new PIXI.Application();
46
+ appRef.current = app, app.init({
47
+ backgroundAlpha: 0,
48
+ resolution: 1,
49
+ preserveDrawingBuffer: !0
50
+ }).then(() => {
51
+ appRef.current === app && (node.appendChild(app.canvas), setApplication(app));
52
+ }).catch(() => {
53
+ appRef.current === app && (appRef.current = null);
54
+ });
55
+ }
56
+ !node && appRef.current && (appRef.current.destroy(!0), appRef.current = null);
57
+ }, [setApplication]), stopAll = () => {
40
58
  stopBlur(), stopCrop(currentOperation === "CROP"), stopResize(currentOperation === "RESIZE");
41
59
  }, handleSave = async () => {
42
60
  try {
@@ -83,12 +101,8 @@ const ImageEditor = ({
83
101
  /* @__PURE__ */ jsx(Modal.Body, { className: "d-flex flex-column align-items-center", children: /* @__PURE__ */ jsxs("div", { className: "d-flex flex-column gap-12 w-100 flex-grow-1", children: [
84
102
  /* @__PURE__ */ jsx(ImageEditorToolbar, { handle: handleOperation, historyCount }),
85
103
  /* @__PURE__ */ jsxs("div", { className: "position-relative d-flex flex-column align-items-center justify-content-center flex-grow-1 w-100 image-editor", children: [
86
- /* @__PURE__ */ jsx(Stage, { onMount: (app) => setApplication(app), options: {
87
- preserveDrawingBuffer: !0,
88
- backgroundAlpha: 0,
89
- resolution: 1
90
- } }),
91
- !!loading && /* @__PURE__ */ jsx("div", { className: "position-absolute top-0 start-0 bottom-0 end-0 m-10 d-flex align-items-center justify-content-center bg-black opacity-25", children: /* @__PURE__ */ jsx(LoadingScreen, {}) })
104
+ /* @__PURE__ */ jsx("div", { ref: containerRef, className: "d-flex justify-content-center w-100" }),
105
+ loading && /* @__PURE__ */ jsx("div", { className: "position-absolute top-0 start-0 bottom-0 end-0 m-10 d-flex align-items-center justify-content-center bg-black opacity-25", children: /* @__PURE__ */ jsx(LoadingScreen, {}) })
92
106
  ] }),
93
107
  /* @__PURE__ */ jsxs("div", { className: "d-flex flex-column flex-md-row m-10 gap-12 w-100", children: [
94
108
  /* @__PURE__ */ jsxs(FormControl, { id: "alt", className: "flex-grow-1", children: [
@@ -1,18 +1,20 @@
1
1
  import * as PIXI from 'pixi.js';
2
2
  /**
3
- * This function start the blur controler
3
+ * This function starts the blur controller:
4
4
  * - drawing cursor
5
5
  * - listening mouse move to move cursor
6
- * - listening
6
+ * - listening pointer events to apply blur
7
7
  *
8
- * @param application
9
- * @param param1
8
+ * @param application The PIXI.Application context
9
+ * @param spriteName The name of the sprite identifying the original image
10
10
  */
11
11
  export declare function start(application: PIXI.Application, { spriteName }: {
12
12
  spriteName: string;
13
13
  }): void;
14
14
  /**
15
- * This function remove cursor and all mouse event listeners
15
+ * This function removes cursor and all mouse event listeners.
16
+ * The blur layer is kept on stage so it gets captured by toBlob/generateTexture
17
+ * when the image is saved or another operation is applied.
16
18
  *
17
19
  * @param application the PIXI.Application context
18
20
  */
@@ -1,45 +1,53 @@
1
1
  import * as PIXI from "pixi.js";
2
2
  import { aggregate } from "../utilities/aggregate.js";
3
+ import { BLUR_LAYER_NAME } from "./constants.js";
3
4
  import { getApplicationScale } from "./misc.js";
4
5
  const BRUSH_SIZE = 20, DEBOUNCE = 50, CURSOR_NAME = "BRUSH_CURSOR";
6
+ function getOrCreateBlurLayer(application) {
7
+ let layer = application.stage.getChildByLabel(BLUR_LAYER_NAME);
8
+ return layer || (layer = new PIXI.Container(), layer.label = BLUR_LAYER_NAME, application.stage.addChild(layer)), layer;
9
+ }
5
10
  function drawBrush(points, scale) {
6
11
  const container = new PIXI.Graphics();
7
12
  for (const point of points)
8
- point && (container.beginFill(16777215, 1), container.drawCircle(point.x, point.y, BRUSH_SIZE / scale), container.lineStyle(0), container.endFill());
13
+ point && (container.circle(point.x, point.y, BRUSH_SIZE / scale), container.fill({
14
+ color: 16777215,
15
+ alpha: 1
16
+ }));
9
17
  return container;
10
18
  }
11
19
  function drawBlurListener(application, {
12
20
  spriteName
13
21
  }) {
14
22
  return aggregate(DEBOUNCE, (event) => application.stage.toLocal(event.global), (points) => {
15
- const child = application.stage.getChildByName(spriteName), scale = getApplicationScale(application);
16
- if (child == null) return;
17
- const newSprite = new PIXI.Sprite(child.texture);
18
- newSprite.filters = [
19
- new PIXI.BlurFilter(
20
- 8,
21
- // PIXI Default value for strength of the blur effect
22
- 4,
23
- // Quality of the blur effect depending on the scale (4 is the PIXI default value)
24
- Math.min(scale, 1)
25
- )
26
- // Resolution of the blur effect depending on the scale
27
- ], newSprite.width = child.width, newSprite.height = child.height, newSprite.scale = new PIXI.Point(1, 1), newSprite.anchor = child.anchor, newSprite.mask = drawBrush(points, scale), child.addChild(newSprite);
23
+ const child = application.stage.getChildByLabel(spriteName), scale = getApplicationScale(application);
24
+ if (!child) return;
25
+ const sprite = child, newSprite = new PIXI.Sprite(sprite.texture);
26
+ newSprite.filters = [new PIXI.BlurFilter({
27
+ strength: 8,
28
+ quality: Math.min(Math.round(4 * scale), 4),
29
+ resolution: Math.min(scale, 1)
30
+ })], newSprite.width = sprite.width, newSprite.height = sprite.height, newSprite.scale = new PIXI.Point(1, 1), newSprite.anchor = sprite.anchor;
31
+ const blurLayer = getOrCreateBlurLayer(application), brushMask = drawBrush(points, scale);
32
+ blurLayer.addChild(brushMask), newSprite.mask = brushMask, blurLayer.addChild(newSprite);
28
33
  });
29
34
  }
30
35
  function drawCursor(application) {
31
36
  removeCursor(application);
32
37
  const scale = getApplicationScale(application), circle = new PIXI.Graphics();
33
- return circle.lineStyle(Math.max(1, 1 / scale), 16711680), circle.drawCircle(0, 0, BRUSH_SIZE / scale), circle.endFill(), circle.name = CURSOR_NAME, application.stage.addChild(circle), circle;
38
+ return circle.circle(0, 0, BRUSH_SIZE / scale), circle.stroke({
39
+ width: Math.max(1, 1 / scale),
40
+ color: 16711680
41
+ }), circle.label = CURSOR_NAME, application.stage.addChild(circle), circle;
34
42
  }
35
43
  function removeCursor(application) {
36
- const child = application.stage.getChildByName(CURSOR_NAME);
44
+ const child = application.stage.getChildByLabel(CURSOR_NAME);
37
45
  child && child.removeFromParent();
38
46
  }
39
47
  function moveCursorListener(application) {
40
48
  return (event) => {
41
49
  if (!application) return;
42
- const point = application.stage.toLocal(event.global), child = application.stage.getChildByName(CURSOR_NAME, !0);
50
+ const point = application.stage.toLocal(event.global), child = application.stage.getChildByLabel(CURSOR_NAME, !0);
43
51
  child && (child.position.x = point.x, child.position.y = point.y);
44
52
  };
45
53
  }
@@ -53,7 +61,7 @@ function start(application, {
53
61
  spriteName
54
62
  });
55
63
  application.stage.on("pointerdown", () => {
56
- application.stage.on("pointermove", blurListener);
64
+ application.stage.off("pointermove", blurListener), application.stage.on("pointermove", blurListener);
57
65
  });
58
66
  const stopListening = () => {
59
67
  var _a;
@@ -0,0 +1,2 @@
1
+ /** Label used for the blur layer Container in the stage */
2
+ export declare const BLUR_LAYER_NAME = "BLUR_LAYER";
@@ -0,0 +1,4 @@
1
+ const BLUR_LAYER_NAME = "BLUR_LAYER";
2
+ export {
3
+ BLUR_LAYER_NAME
4
+ };
@@ -7,7 +7,7 @@ export type CornerType = 'TOP_LEFT' | 'TOP_RIGHT' | 'BOTTOM_LEFT' | 'BOTTOM_RIGH
7
7
  * - A mask applied to the original image
8
8
  *
9
9
  * @param application The PIXI.Application context
10
- * @param {spriteName:string} {spriteName} The sprite name identifying the original image
10
+ * @param spriteName The sprite name identifying the original image
11
11
  */
12
12
  export declare function start(application: PIXI.Application, { spriteName }: {
13
13
  spriteName: string;
@@ -19,10 +19,9 @@ export declare function start(application: PIXI.Application, { spriteName }: {
19
19
  */
20
20
  export declare function stop(application: PIXI.Application): void;
21
21
  /**
22
- * This function apply a crop to the imageSrc and return the result as a PIXI.Sprite object
22
+ * This function applies a crop and returns the result as a PIXI.Sprite object
23
23
  *
24
- * @param application
25
- * @param imageSrc a string URI of the image to crop
26
- * @returns a PIXI.Sprite with a imageSrc cropped or undefined if the image has not been cropped
24
+ * @param application The PIXI.Application context
25
+ * @returns a PIXI.Sprite with the cropped image or undefined if no crop area was defined
27
26
  */
28
27
  export declare function save(application: PIXI.Application): PIXI.Sprite | undefined;