@kwiz/fluentui 1.0.77 → 1.0.79

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 (90) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +53 -53
  3. package/dist/controls/svg.js +21 -21
  4. package/dist/controls/svg.js.map +1 -1
  5. package/dist/helpers/hooks.d.ts +1 -1
  6. package/dist/helpers/hooks.js +43 -30
  7. package/dist/helpers/hooks.js.map +1 -1
  8. package/package.json +85 -85
  9. package/.dependency-cruiser.js +0 -399
  10. package/.github/workflows/npm-publish.yml +0 -24
  11. package/dist/@types/forwardRef.d.ts +0 -0
  12. package/dist/@types/forwardRef.js +0 -1
  13. package/dist/@types/forwardRef.js.map +0 -1
  14. package/dist/controls/error-boundary copy.d.ts +0 -23
  15. package/dist/controls/error-boundary copy.js +0 -33
  16. package/dist/controls/error-boundary copy.js.map +0 -1
  17. package/dist/helpers/common.d.ts +0 -4
  18. package/dist/helpers/common.js +0 -2
  19. package/dist/helpers/common.js.map +0 -1
  20. package/dist/helpers/context.d.ts +0 -26
  21. package/dist/helpers/context.js +0 -15
  22. package/dist/helpers/context.js.map +0 -1
  23. package/dist/helpers/drag-drop/exports.d.ts +0 -12
  24. package/dist/helpers/drag-drop/exports.js +0 -3
  25. package/dist/helpers/drag-drop/exports.js.map +0 -1
  26. package/dist/helpers/exports.d.ts +0 -7
  27. package/dist/helpers/exports.js +0 -8
  28. package/dist/helpers/exports.js.map +0 -1
  29. package/src/_modules/config.ts +0 -9
  30. package/src/_modules/constants.ts +0 -3
  31. package/src/controls/ColorPickerDialog.tsx +0 -84
  32. package/src/controls/accordion.tsx +0 -62
  33. package/src/controls/button.tsx +0 -181
  34. package/src/controls/canvas/CustomEventTargetBase.ts +0 -33
  35. package/src/controls/canvas/DrawPad.tsx +0 -297
  36. package/src/controls/canvas/DrawPadManager.ts +0 -695
  37. package/src/controls/canvas/bezier.ts +0 -110
  38. package/src/controls/canvas/point.ts +0 -45
  39. package/src/controls/card-list.tsx +0 -32
  40. package/src/controls/card.tsx +0 -78
  41. package/src/controls/centered.tsx +0 -15
  42. package/src/controls/date.tsx +0 -88
  43. package/src/controls/diagram-picker.tsx +0 -97
  44. package/src/controls/divider.tsx +0 -16
  45. package/src/controls/dropdown.tsx +0 -67
  46. package/src/controls/error-boundary.tsx +0 -42
  47. package/src/controls/field-editor.tsx +0 -43
  48. package/src/controls/file-upload.tsx +0 -156
  49. package/src/controls/horizontal.tsx +0 -49
  50. package/src/controls/html-editor/editor.tsx +0 -182
  51. package/src/controls/index.ts +0 -33
  52. package/src/controls/input.tsx +0 -161
  53. package/src/controls/kwizoverflow.tsx +0 -107
  54. package/src/controls/list.tsx +0 -120
  55. package/src/controls/loading.tsx +0 -11
  56. package/src/controls/menu.tsx +0 -196
  57. package/src/controls/merge-text.tsx +0 -126
  58. package/src/controls/please-wait.tsx +0 -33
  59. package/src/controls/progress-bar.tsx +0 -110
  60. package/src/controls/prompt.tsx +0 -122
  61. package/src/controls/qrcode.tsx +0 -37
  62. package/src/controls/search.tsx +0 -72
  63. package/src/controls/section.tsx +0 -134
  64. package/src/controls/svg.tsx +0 -139
  65. package/src/controls/toolbar.tsx +0 -47
  66. package/src/controls/vertical-content.tsx +0 -50
  67. package/src/controls/vertical.tsx +0 -43
  68. package/src/helpers/block-nav.tsx +0 -89
  69. package/src/helpers/context-const.ts +0 -30
  70. package/src/helpers/context-export.tsx +0 -78
  71. package/src/helpers/context-internal.ts +0 -14
  72. package/src/helpers/drag-drop/drag-drop-container.tsx +0 -54
  73. package/src/helpers/drag-drop/drag-drop-context-internal.tsx +0 -10
  74. package/src/helpers/drag-drop/drag-drop-context.tsx +0 -62
  75. package/src/helpers/drag-drop/drag-drop.types.ts +0 -21
  76. package/src/helpers/drag-drop/index.ts +0 -12
  77. package/src/helpers/drag-drop/readme.md +0 -76
  78. package/src/helpers/drag-drop/use-draggable.ts +0 -48
  79. package/src/helpers/drag-drop/use-droppable.ts +0 -39
  80. package/src/helpers/forwardRef.ts +0 -7
  81. package/src/helpers/hooks-events.ts +0 -150
  82. package/src/helpers/hooks.tsx +0 -163
  83. package/src/helpers/index.ts +0 -8
  84. package/src/helpers/use-alerts.tsx +0 -75
  85. package/src/helpers/use-editable-control.tsx +0 -38
  86. package/src/helpers/use-toast.tsx +0 -30
  87. package/src/index.ts +0 -3
  88. package/src/styles/index.ts +0 -1
  89. package/src/styles/styles.ts +0 -105
  90. package/src/styles/theme.ts +0 -91
@@ -1,84 +0,0 @@
1
- import { Field } from "@fluentui/react-components";
2
- import { ColorRegular } from "@fluentui/react-icons";
3
- import { isFunction, isNullOrEmptyString, isNumber } from "@kwiz/common";
4
- import * as React from "react";
5
- import ColorPicker, { Color } from 'react-pick-color';
6
- import { useEffectOnlyOnMount, useStateEX } from "../helpers";
7
- import { ButtonEX } from "./button";
8
- import { InputEx } from "./input";
9
- import { Prompter } from "./prompt";
10
- export interface iProps {
11
- label?: string;
12
- value: string;
13
- onChange: (newValue: string) => void;
14
- required?: boolean;
15
- showValidationErrors?: boolean;
16
- underlined?: boolean;
17
- width?: number;
18
- buttonOnly?: boolean;
19
- placeholder?: string;
20
- disabled?: boolean;
21
-
22
- /** specify a specific mount node for this dialog */
23
- mountNode?: HTMLElement | null | {
24
- element?: HTMLElement | null;
25
- className?: string;
26
- }
27
- }
28
-
29
- export const ColorPickerEx: React.FunctionComponent<iProps> = (props) => {
30
- const [isOpen, setIsOpen] = useStateEX<boolean>(false);
31
- const [selectedColor, setSelectedColor] = useStateEX<string>(props.value);
32
-
33
- const getColorCells = React.useCallback(() => {
34
- let cells: Color[] = [
35
- "white", "black"
36
- ];
37
- return cells;
38
- }, useEffectOnlyOnMount);
39
- return <>
40
- {props.buttonOnly
41
- ? <ButtonEX disabled={props.disabled}
42
- title="Open color picker"
43
- icon={<ColorRegular
44
- color={selectedColor} />
45
- }
46
- onClick={(e) => setIsOpen(true)} />
47
- : <Field label={props.label}
48
- required={props.required === true}
49
- validationMessage={props.showValidationErrors && props.required === true && isNullOrEmptyString(selectedColor) ? "You can't leave this blank." : undefined}
50
- >
51
- <InputEx disabled={props.disabled}
52
- placeholder={props.placeholder || "Enter value here"}
53
- style={isNumber(props.width) ? { width: props.width } : undefined}
54
- value={selectedColor}
55
- onChange={(e, data) => {
56
- setSelectedColor(data.value);
57
- if (isFunction(props.onChange)) {
58
- props.onChange(data.value);
59
- }
60
- }}
61
- contentAfter={<ButtonEX disabled={props.disabled}
62
- title="Open color picker"
63
- icon={<ColorRegular
64
- color={selectedColor} />
65
- }
66
- onClick={(e) => setIsOpen(true)} />
67
- }
68
- />
69
- </Field>}
70
- {isOpen && <Prompter maxWidth={332} mountNode={props.mountNode}
71
- hideOk hideCancel onCancel={() => {
72
- if (isFunction(props.onChange)) {
73
- props.onChange(selectedColor);
74
- }
75
- setIsOpen(false);
76
- }} showCancelInTitle
77
- title={props.label || "Choose a color"}
78
- >
79
- <ColorPicker color={selectedColor} onChange={color => setSelectedColor(color.hex)}
80
- presets={getColorCells()}
81
- />
82
- </Prompter>}
83
- </>;
84
- };
@@ -1,62 +0,0 @@
1
- import { makeStyles } from "@fluentui/react-components";
2
- import { ChevronRightRegular } from "@fluentui/react-icons";
3
- import * as React from 'react';
4
- import { KnownClassNames } from "../styles/styles";
5
- import { ButtonEX } from "./button";
6
- import { DividerEX } from "./divider";
7
- import { Horizontal } from "./horizontal";
8
- import { Vertical } from "./vertical";
9
-
10
- const useStyles = makeStyles({
11
- opened: {
12
- transform: "rotate(90deg)",
13
- transition: "transform 200ms ease-out"
14
- },
15
- header: {
16
- paddingLeft: 0
17
- },
18
- root: {
19
- maxHeight: "100%"
20
- },
21
- rootFill: {
22
- minHeight: "100%"
23
- },
24
- body: {
25
- overflow: "auto",
26
- }
27
- });
28
-
29
- interface iProps {
30
- /** optional: send the key for the group you want to open by default */
31
- opened?: string;
32
- fillHeight?: boolean;
33
- groups: {
34
- key: string;
35
- title: string;
36
- icon?: JSX.Element;
37
- content: JSX.Element;
38
- }[];
39
- }
40
- export const AccordionEX: React.FunctionComponent<iProps> = (props) => {
41
- const classes = useStyles();
42
- const [opened, setOpened] = React.useState(props.opened || props.groups[0].key);
43
- return (<Vertical main css={[classes.root, props.fillHeight && classes.rootFill, KnownClassNames.accordion]}>
44
- {props.groups.map(group => <React.Fragment key={group.key}>
45
- <ButtonEX className={`${classes.header} ${KnownClassNames.accordionHeader} ${opened === group.key ? ` ${KnownClassNames.isOpen}` : ''}`}
46
- icon={<ChevronRightRegular className={opened === group.key ? classes.opened : ''} />}
47
- title={group.title} showTitleWithIcon dontCenterText
48
- onClick={() => setOpened(group.key)}
49
- />
50
- <DividerEX />
51
- {group.key === opened && <>
52
- <Horizontal main css={[classes.body, KnownClassNames.accordionBodyWrapper]}>
53
- <Vertical main css={[KnownClassNames.accordionBody]}>
54
- {group.content}
55
- </Vertical>
56
- </Horizontal>
57
- <DividerEX />
58
- </>}
59
- </React.Fragment>)}
60
- </Vertical>
61
- );
62
- }
@@ -1,181 +0,0 @@
1
- import { Button, ButtonProps, CompoundButton, compoundButtonClassNames, CompoundButtonProps, makeStyles, mergeClasses, tokens, Tooltip } from '@fluentui/react-components';
2
- import { capitalizeFirstLetter, isFunction, isNullOrEmptyString, isNullOrUndefined, isString, PushNoDuplicate } from '@kwiz/common';
3
- import React from 'react';
4
- import { useKWIZFluentContext } from '../helpers/context-internal';
5
- import { commonSizes, KnownClassNames } from '../styles/styles';
6
-
7
- interface IProps {
8
- title: string;//required
9
- showTitleWithIcon?: boolean;
10
- dontStretch?: boolean;
11
- hideOnPrint?: boolean;
12
- dontCenterText?: boolean;
13
- hoverIcon?: JSX.Element;
14
- hoverTitle?: string;
15
- onClick?: (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => void | undefined;//type the onClick arg
16
- }
17
- interface IPropsCompound extends IProps {
18
- width?: string | number;
19
- }
20
-
21
- export type ButtonEXProps = IProps & Omit<ButtonProps, "onClick" | "title">;
22
- export type CompoundButtonEXProps = IPropsCompound & Omit<CompoundButtonProps, "onClick" | "title">;;
23
-
24
- const useStyles = makeStyles({
25
- buttonNoCenter: {
26
- justifyContent: 'flex-start',
27
- '& *': {
28
- /* a button with no center that has content of a vertical, or multiple labels */
29
- alignItems: 'flex-start'
30
- }
31
- },
32
- danger: {
33
- backgroundColor: tokens.colorStatusDangerBackground1,
34
- color: tokens.colorStatusWarningForeground2,
35
-
36
- [`& .${compoundButtonClassNames.secondaryContent}`]: {
37
- color: tokens.colorStatusWarningForeground1
38
- }
39
- },
40
- success: {
41
- color: tokens.colorStatusSuccessForeground1,
42
-
43
- [`& .${compoundButtonClassNames.secondaryContent}`]: {
44
- color: tokens.colorStatusSuccessForeground1,
45
- }
46
- },
47
- primarySubtle: {
48
- color: tokens.colorBrandForeground1,
49
-
50
- [`& .${compoundButtonClassNames.secondaryContent}`]: {
51
- color: tokens.colorBrandForeground1,
52
- }
53
- },
54
- dangerSubtle: {
55
- color: tokens.colorStatusWarningForeground2,
56
-
57
- [`& .${compoundButtonClassNames.secondaryContent}`]: {
58
- color: tokens.colorStatusWarningForeground1
59
- }
60
- }
61
- })
62
-
63
-
64
- export const ButtonEX = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
65
- const ctx = useKWIZFluentContext();
66
- const [hover, setHover] = React.useState(false);
67
- const trackHover = !isNullOrEmptyString(props.hoverTitle) || !isNullOrUndefined(props.hoverIcon);
68
-
69
- const title = hover && !isNullOrEmptyString(props.hoverTitle) ? props.hoverTitle
70
- : props.title || props['aria-label'];
71
- const icon = hover && !isNullOrUndefined(props.hoverIcon) ? props.hoverIcon : props.icon;
72
- let hasIcon = !isNullOrUndefined(icon);
73
- let hasText = props.children || !hasIcon || (hasIcon && props.showTitleWithIcon === true);
74
-
75
- const cssNames = useStyles();
76
- let css: string[] = [];
77
-
78
- if (props.hideOnPrint) PushNoDuplicate(css, KnownClassNames.printHide);
79
- if (props.dontCenterText) PushNoDuplicate(css, cssNames.buttonNoCenter);
80
-
81
- let btn = <Button ref={ref} appearance='subtle' {...props as any as ButtonProps} className={mergeClasses(...css, props.className)}
82
- aria-label={title} title={undefined} icon={icon}
83
- onMouseEnter={trackHover ? (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
84
- setHover(true);
85
- if (isFunction(props.onMouseEnter))
86
- props.onMouseEnter(e as any);
87
- } : props.onMouseEnter as any}
88
- onMouseLeave={trackHover ? (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
89
- setHover(false);
90
- if (isFunction(props.onMouseLeave))
91
- props.onMouseLeave(e as any);
92
- } : props.onMouseLeave as any}
93
- >{props.children ||
94
- //no icon? will show the title by default
95
- (hasText && capitalizeFirstLetter(title))}</Button>;
96
- if (!hasText || props.children)//icon only or when content is different than props.title
97
- btn = <Tooltip showDelay={1000} relationship='label' withArrow appearance='inverted' content={title}
98
- mountNode={ctx.mountNode}
99
- >
100
- {btn}
101
- </Tooltip>;
102
-
103
- return (
104
- props.dontStretch ? <div>{btn}</div> : btn
105
-
106
- );
107
- });
108
- export const ButtonEXSecondary = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
109
- const ctx = useKWIZFluentContext();
110
- return (
111
- <ButtonEX ref={ref} appearance='secondary' shape={ctx.buttonShape} {...props}></ButtonEX>
112
- );
113
- });
114
- /** to be used in MessageBarActions for prominent actions. Otherwise use ButtonEX or ButtonEXDangerSubtle */
115
- export const ButtonEXMessageBarAction = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
116
- const ctx = useKWIZFluentContext();
117
- return (
118
- <ButtonEX ref={ref} appearance='secondary' hideOnPrint {...props}></ButtonEX>
119
- );
120
- });
121
- export const ButtonEXPrimary = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
122
- return (
123
- <ButtonEXSecondary ref={ref} appearance='primary' {...props}>{props.children}</ButtonEXSecondary>
124
- );
125
- });
126
- export const ButtonEXDanger = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
127
- const cssNames = useStyles();
128
- return (
129
- <ButtonEXSecondary ref={ref} className={props.disabled ? undefined : cssNames.danger} {...props}>{props.children}</ButtonEXSecondary>
130
- );
131
- });
132
- export const ButtonEXSuccess = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
133
- const cssNames = useStyles();
134
- return (
135
- <ButtonEX ref={ref} className={cssNames.success} {...props}>{props.children}</ButtonEX>
136
- );
137
- });
138
- export const ButtonEXPrimarySubtle = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
139
- const cssNames = useStyles();
140
- return (
141
- <ButtonEX ref={ref} className={props.disabled ? undefined : cssNames.primarySubtle} {...props}>{props.children}</ButtonEX>
142
- );
143
- });
144
- export const ButtonEXDangerSubtle = React.forwardRef<HTMLButtonElement, (ButtonEXProps)>((props, ref) => {
145
- const cssNames = useStyles();
146
- return (
147
- <ButtonEX ref={ref} className={props.disabled ? undefined : cssNames.dangerSubtle} {...props}>{props.children}</ButtonEX>
148
- );
149
- });
150
-
151
- export const CompoundButtonEX = React.forwardRef<HTMLButtonElement, (CompoundButtonEXProps)>((props, ref) => {
152
- const ctx = useKWIZFluentContext();
153
- let title = props.title || props['aria-label'];
154
- let tooltip = isString(props.secondaryContent) ? props.secondaryContent : title;
155
- let max = typeof (props.width) === "undefined" ? commonSizes.widthMedium : props.width;
156
- return (
157
- <Tooltip showDelay={1000} relationship='label' withArrow appearance='inverted' content={tooltip}
158
- mountNode={ctx.mountNode}
159
- >
160
- <CompoundButton ref={ref} appearance='subtle' style={{ justifyContent: "flex-start", maxWidth: max }} {...props as any as CompoundButtonProps} aria-label={tooltip} title={undefined}>
161
- {props.children || capitalizeFirstLetter(title)}</CompoundButton>
162
- </Tooltip>
163
- );
164
- });
165
- export const CompoundButtonEXSecondary = React.forwardRef<HTMLButtonElement, (CompoundButtonEXProps)>((props, ref) => {
166
- const ctx = useKWIZFluentContext();
167
- return (
168
- <CompoundButtonEX ref={ref} appearance='secondary' shape={ctx.buttonShape} {...props}></CompoundButtonEX>
169
- );
170
- });
171
- export const CompoundButtonEXPrimary = React.forwardRef<HTMLButtonElement, (CompoundButtonEXProps)>((props, ref) => {
172
- return (
173
- <CompoundButtonEXSecondary ref={ref} appearance='primary' {...props}>{props.children}</CompoundButtonEXSecondary>
174
- );
175
- });
176
- export const CompoundButtonEXDanger = React.forwardRef<HTMLButtonElement, (CompoundButtonEXProps)>((props, ref) => {
177
- const cssNames = useStyles();
178
- return (
179
- <CompoundButtonEXSecondary ref={ref} className={cssNames.danger} {...props}>{props.children}</CompoundButtonEXSecondary>
180
- );
181
- });
@@ -1,33 +0,0 @@
1
- export class CustomEventTargetBase implements EventTarget {
2
- private _et: EventTarget;
3
-
4
- public constructor() {
5
- try {
6
- this._et = new EventTarget();
7
- } catch (error) {
8
- // Using document as EventTarget to support iOS 13 and older.
9
- // Because EventTarget constructor just exists at iOS 14 and later.
10
- this._et = document;
11
- }
12
- }
13
-
14
- public addEventListener(
15
- type: string,
16
- listener: EventListenerOrEventListenerObject | null,
17
- options?: boolean | AddEventListenerOptions,
18
- ): void {
19
- this._et.addEventListener(type, listener, options);
20
- }
21
-
22
- public dispatchEvent(event: Event): boolean {
23
- return this._et.dispatchEvent(event);
24
- }
25
-
26
- public removeEventListener(
27
- type: string,
28
- callback: EventListenerOrEventListenerObject | null,
29
- options?: boolean | EventListenerOptions,
30
- ): void {
31
- this._et.removeEventListener(type, callback, options);
32
- }
33
- }
@@ -1,297 +0,0 @@
1
- import { Field, tokens } from "@fluentui/react-components";
2
- import { ArrowMaximizeRegular, ArrowMinimizeRegular, ArrowUploadRegular, CalligraphyPenRegular, DismissRegular } from "@fluentui/react-icons";
3
- import { debounce, getCSSVariableValue, ImageFileTypes, isElement, isNotEmptyString, isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined } from "@kwiz/common";
4
- import * as React from "react";
5
- import { useAlerts, useElementSize, useStateEX } from "../../helpers";
6
- import { ButtonEX } from "../button";
7
- import { ColorPickerEx } from "../ColorPickerDialog";
8
- import { FileUpload } from "../file-upload";
9
- import { Horizontal } from "../horizontal";
10
- import { InputEx } from "../input";
11
- import { Vertical } from "../vertical";
12
- import DrawPadManager from "./DrawPadManager";
13
-
14
- interface iProps {
15
- BackgroundColor?: string;
16
- BorderColor?: string;
17
- LineColor?: string;
18
- minWidth?: number;
19
- minHeight?: number;
20
- /** url or base64 image data:image/png;base64,.... */
21
- Value?: string;
22
- /** when user hits clear, it will reset to this value
23
- * url or base64 image data:image/png;base64,....
24
- */
25
- DefaultBackdrop?: string;
26
- OnChange?: (newValue: string) => void;
27
- ReadOnly?: boolean;
28
- HideUpload?: boolean;
29
- HideClear?: boolean;
30
- //HideUndo?: boolean;
31
- HideColorPicker?: boolean;
32
- disabled?: boolean;
33
- /** true - will prompt user for his name, string will just sign as that string */
34
- allowSigning?: boolean | string;
35
- allowFullscreen?: boolean;
36
- }
37
- var _userName: string = null;
38
- export const DrawPadUserName = {
39
- get: () => { return _userName },
40
- set: (userName: string) => { _userName = userName }
41
- };
42
-
43
- const fontName = "Dancing Script";
44
- let fontLoading: Promise<boolean> = null;
45
- let fontReady = false;
46
- export const DrawPad: React.FunctionComponent<iProps> = (props) => {
47
- const [LineColor, setLineColor] = useStateEX<string>(props.LineColor || tokens.colorBrandForeground1);
48
- const [manager, setmanager] = useStateEX<DrawPadManager>(null);
49
- const [canUndo, setcanUndo] = useStateEX<boolean>(false, { skipUpdateIfSame: true });
50
- const [signed, setSigned] = useStateEX<boolean>(false);
51
- const [fullscreen, setFullscreen] = useStateEX<boolean>(false);
52
- const onChangeRef = React.useRef(props.OnChange);
53
- const alerts = useAlerts();
54
- const canvasArea: React.RefObject<HTMLCanvasElement> = React.useRef();
55
- const canvasContainerDiv = React.useRef<HTMLDivElement>();
56
-
57
- //keep onChange up to date
58
- React.useEffect(() => {
59
- onChangeRef.current = props.OnChange;
60
- }, [props.OnChange]);
61
- //if user name provided - keep it
62
- React.useEffect(() => {
63
- if (isNotEmptyString(props.allowSigning)) {
64
- DrawPadUserName.set(props.allowSigning);
65
- }
66
- }, [props.allowSigning]);
67
-
68
- //load font for sign as text, if needed
69
- React.useEffect(() => {
70
- if (props.allowSigning && !fontLoading) {
71
- let DancingScriptFont = new FontFace(
72
- fontName,
73
- "url(https://fonts.gstatic.com/s/dancingscript/v25/If2RXTr6YS-zF4S-kcSWSVi_szLgiuE.woff2) format('woff2')",
74
- {
75
- style: "normal",
76
- weight: "400 700",
77
- display: "swap",
78
- unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
79
- }
80
- );
81
-
82
- fontLoading = DancingScriptFont.load().then(async loadedFont => {
83
- document.fonts.add(loadedFont);
84
- await document.fonts.ready;
85
- fontReady = true;
86
- return true;
87
- });
88
- }
89
- }, [props.allowSigning]);
90
-
91
- //setup manager
92
- React.useEffect(() => {
93
- if (!props.ReadOnly && canvasArea.current && !canvasArea.current["canvas_initialized"]) {
94
- canvasArea.current["canvas_initialized"] = true;//for some reason this still fires twice on the same div
95
- //this gets called after each render...
96
- let manager = new DrawPadManager(canvasArea.current);
97
- setmanager(manager);
98
- manager.addEventListener("endStroke", () => {
99
- //because this is used in an addEventListener - need to have a ref to get the most up to date onChange
100
- if (onChangeRef.current) {
101
- let canvasValue = manager.toPng();
102
- setcanUndo(true);
103
- onChangeRef.current(canvasValue);
104
- }
105
- });
106
- }
107
- }, [canvasArea]);
108
- //update line color selection
109
- React.useEffect(() => {
110
- if (manager) {
111
- manager.penColor.value = getCSSVariableValue(LineColor, canvasArea.current);
112
- }
113
- }, [manager, LineColor]);
114
-
115
- //Set props value to canvas on initial load or when owner element changes the prop
116
- React.useEffect(() => {
117
- if (manager) {
118
- let canvasValue = manager.toPng();
119
- let neededValue = isNotEmptyString(props.Value)
120
- ? props.Value
121
- : isNotEmptyString(props.DefaultBackdrop)
122
- ? props.DefaultBackdrop
123
- : "";
124
- if (canvasValue !== neededValue ||
125
- (
126
- isNullOrEmptyString(canvasValue) &&
127
- isNullOrEmptyString(neededValue)
128
- )
129
- ) {
130
- UpdateCanvas(neededValue);//if called repeatedly or too fast - may not load correctly
131
- }
132
- }
133
- });//cant use those - canvas loses value on drag so must run this every time: [props.Value, props.DefaultBackdrop, manager]);
134
-
135
- //set value to canvas
136
- const UpdateCanvas = React.useCallback(debounce((valueToSet: string) => {
137
- if (valueToSet === "") manager.clear();
138
- else manager.fromDataURL(valueToSet);
139
- }, 200, this), [manager]);
140
-
141
- //enable/disable canvas manager
142
- React.useEffect(() => {
143
- if (manager) {
144
- if (props.disabled) manager.off();//stop accepting strokes, but still allow to set a default value
145
- else manager.on();
146
- }
147
- }, [manager, props.disabled]);
148
-
149
- const sign = React.useCallback((name: string) => {
150
- let canvas = canvasArea.current;
151
- if (!isElement(canvas)) {
152
- return;
153
- }
154
- setSigned(true);
155
-
156
- let height = canvas.clientHeight;
157
- let width = canvas.clientWidth;
158
-
159
- let ctx = canvas.getContext("2d");
160
- ctx.fillStyle = getCSSVariableValue(LineColor, canvasArea.current);
161
-
162
- let fontSize = 0.6 * height;
163
- ctx.font = `${fontSize}px ${fontName}`;
164
- let textMeasurement = ctx.measureText(name);
165
- let textWidth = textMeasurement.width;
166
- let maxWidth = 0.9 * width;
167
-
168
- while (textWidth > maxWidth && fontSize > 1) {
169
- fontSize = fontSize - 1;
170
- ctx.font = `${fontSize}px ${fontName}`;
171
- textMeasurement = ctx.measureText(name);
172
- textWidth = textMeasurement.width;
173
- }
174
-
175
- let x = (width - textWidth) / 2;
176
- let y = 0.6 * height; //baseline not starting point
177
- ctx.fillText(name, x, y, width);
178
- let url = canvas.toDataURL("image/png");
179
- onChangeRef.current?.(url);
180
- }, [canvasArea, LineColor]);
181
-
182
- const onSignAs = React.useCallback(async () => {
183
- if (isNullOrUndefined(DrawPadUserName.get())) {
184
- //prompt user to type his name - then continue
185
- alerts.promptEX({
186
- //mountNode: canvasContainerDiv.current, this lets other content on the form cover the dialog
187
- title: "Sign as name",
188
- children: <Field label="Signing as" hint="Please type in your name" required>
189
- <InputEx onChange={(e, data) => DrawPadUserName.set(data.value)} />
190
- </Field>,
191
- onCancel: () => {
192
- DrawPadUserName.set(null);//get rid of anything they typed while dialog was open
193
- },
194
- onOK: () => {
195
- if (!isNullOrEmptyString(DrawPadUserName.get()))//need to test current since this won't be updated when state changes
196
- {
197
- sign(DrawPadUserName.get());
198
- }
199
- },
200
- });
201
- }
202
- else sign(DrawPadUserName.get());
203
- }, [canvasArea, LineColor]);
204
-
205
- const HideButtons = props.HideClear && props.HideColorPicker && props.HideUpload;
206
-
207
- const sizer = useElementSize(canvasContainerDiv.current);
208
- const [size, setSize] = useStateEX<{ width?: number; height?: number }>({});
209
- //handle canvas resizing
210
- React.useEffect(() => {
211
- if (canvasContainerDiv.current) {
212
- setSize({
213
- width: canvasContainerDiv.current.clientWidth,
214
- height: canvasContainerDiv.current.clientHeight,
215
- });
216
- if (manager) manager.resizeCanvas();
217
- }
218
- }, [canvasContainerDiv, sizer, manager]);
219
-
220
- return <Horizontal nogap fullscreen={fullscreen}>
221
- {alerts.alertPrompt}
222
- <div ref={canvasContainerDiv}
223
- style={{
224
- flexGrow: 1,
225
- position: "relative",
226
- minWidth: props.minWidth,
227
- minHeight: props.minHeight,
228
- backgroundColor: props.BackgroundColor,
229
- border: `1px solid ${props.BorderColor || tokens.colorNeutralStroke1}`
230
- }}>
231
- {props.ReadOnly
232
- ? <img src={isNotEmptyString(props.Value) ? props.Value : props.DefaultBackdrop} style={{ position: "absolute", left: 0, top: 0, width: size.width, height: size.height }} />
233
- :
234
- <div style={{ position: "absolute", left: 0, top: 0, width: size.width, height: size.height }}>
235
- <canvas
236
- ref={canvasArea}
237
- style={{
238
- touchAction: "none",
239
- userSelect: "none",
240
- position: "absolute",
241
- left: 0,
242
- top: 0,
243
- width: size.width,
244
- height: size.height,
245
- border: tokens.colorBrandStroke1
246
- }} />
247
- {!signed
248
- && !isNullOrEmptyString(props.allowSigning)
249
- && !isNullOrEmptyArray(fontReady)
250
- && <ButtonEX
251
- style={{
252
- position: "absolute",
253
- bottom: 0,
254
- border: 0,
255
- margin: 0,
256
- right: 0,
257
- height: 16
258
- }}
259
- disabled={props.disabled}
260
- icon={<CalligraphyPenRegular />}
261
- title={`Sign as ${props.allowSigning === true ? "..." : props.allowSigning}`}
262
- onClick={() => {
263
- onSignAs();
264
- }}
265
- />}
266
- </div>
267
- }
268
- </div>
269
- {!props.ReadOnly && !HideButtons && <Vertical nogap>
270
- {props.HideColorPicker || <ColorPickerEx //mountNode={canvasContainerDiv.current} this lets other content on the form cover the dialog
271
- disabled={props.disabled} buttonOnly value={props.LineColor} onChange={newColor => {
272
- setLineColor(newColor);
273
- }} />}
274
- {/* todo: undo isn't working properly
275
- {props.HideUndo || <ButtonEX disabled={!canUndo} title="Undo" icon={<ArrowUndoRegular />} onClick={() => {
276
- manager.undoLast();
277
- setcanUndo(manager.canUndo());
278
- }} />} */}
279
- {props.HideClear || <ButtonEX disabled={props.disabled || isNullOrEmptyString(props.Value)} title="Clear" icon={<DismissRegular />} onClick={() => {
280
- //can call clear on the canvas, or can call the onchange which will cause a re-draw
281
- setSigned(false);
282
- onChangeRef.current?.("");
283
- }} />}
284
- {props.HideUpload || <FileUpload disabled={props.disabled} title="Load background image" icon={<ArrowUploadRegular />} limitFileTypes={ImageFileTypes} asBase64={base64 => {
285
- if (onChangeRef.current)
286
- onChangeRef.current?.(base64[0].base64);//this will trigger a change and state update
287
- else
288
- manager?.fromDataURL(base64[0].base64);//this will just set the image to the canvas but won't trigger a change event for the caller
289
- }} />}
290
- {props.allowFullscreen && <ButtonEX title="Full screen" disabled={props.disabled} icon={fullscreen ? <ArrowMinimizeRegular /> : <ArrowMaximizeRegular />} onClick={async () => {
291
- //can call clear on the canvas, or can call the onchange which will cause a re-draw
292
- await setFullscreen(!fullscreen);
293
- if (manager) manager.resizeCanvas();
294
- }} />}
295
- </Vertical>}
296
- </Horizontal>;
297
- }