@kwiz/fluentui 1.0.40 → 1.0.41

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './controls/accordion';
2
2
  export * from './controls/button';
3
+ export * from './controls/canvas/DrawPad';
3
4
  export * from './controls/centered';
5
+ export * from './controls/ColorPickerDialog';
4
6
  export * from './controls/date';
5
7
  export * from './controls/divider';
6
8
  export * from './controls/dropdown';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE5E,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE5E,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kwiz/fluentui",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "KWIZ common controls for FluentUI",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -56,12 +56,13 @@
56
56
  "typescript": "^5.3.3"
57
57
  },
58
58
  "dependencies": {
59
- "@kwiz/common": "^1.0.83",
59
+ "@kwiz/common": "^1.0.84",
60
60
  "esbuild": "^0.19.12",
61
61
  "get-tsconfig": "^4.7.2",
62
62
  "jodit-react": "^4.1.2",
63
63
  "react-dnd": "^16.0.1",
64
64
  "react-dnd-html5-backend": "^16.0.1",
65
+ "react-pick-color": "^2.0.0",
65
66
  "resolve-pkg-maps": "^1.0.0"
66
67
  },
67
68
  "peerDependencies": {
@@ -0,0 +1,76 @@
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/hooks";
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
+ }
21
+
22
+ export const ColorPickerEx: React.FunctionComponent<iProps> = (props) => {
23
+ const [isOpen, setIsOpen] = useStateEX<boolean>(false);
24
+ const [selectedColor, setSelectedColor] = useStateEX<string>(props.value);
25
+
26
+ const getColorCells = React.useCallback(() => {
27
+ let cells: Color[] = [
28
+ "white", "black"
29
+ ];
30
+ return cells;
31
+ }, useEffectOnlyOnMount);
32
+ return <>
33
+ {props.buttonOnly
34
+ ? <ButtonEX
35
+ title="Open color picker"
36
+ icon={<ColorRegular
37
+ color={selectedColor} />
38
+ }
39
+ onClick={() => setIsOpen(true)} />
40
+ : <Field label={props.label}
41
+ required={props.required === true}
42
+ validationMessage={props.showValidationErrors && props.required === true && isNullOrEmptyString(selectedColor) ? "You can't leave this blank." : undefined}
43
+ >
44
+ <InputEx
45
+ placeholder={props.placeholder || "Enter value here"}
46
+ style={isNumber(props.width) ? { width: props.width } : undefined}
47
+ value={selectedColor}
48
+ onChange={(e, data) => {
49
+ setSelectedColor(data.value);
50
+ if (isFunction(props.onChange)) {
51
+ props.onChange(data.value);
52
+ }
53
+ }}
54
+ contentAfter={<ButtonEX
55
+ title="Open color picker"
56
+ icon={<ColorRegular
57
+ color={selectedColor} />
58
+ }
59
+ onClick={() => setIsOpen(true)} />}
60
+ />
61
+ </Field>}
62
+ {isOpen && <Prompter maxWidth={332}
63
+ hideOk hideCancel onCancel={() => {
64
+ if (isFunction(props.onChange)) {
65
+ props.onChange(selectedColor);
66
+ }
67
+ setIsOpen(false);
68
+ }} showCancelInTitle
69
+ title={props.label || "Choose a color"}
70
+ >
71
+ <ColorPicker color={selectedColor} onChange={color => setSelectedColor(color.hex)}
72
+ presets={getColorCells()}
73
+ />
74
+ </Prompter>}
75
+ </>;
76
+ };
@@ -0,0 +1,33 @@
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
+ }
@@ -0,0 +1,195 @@
1
+ import { tokens } from "@fluentui/react-components";
2
+ import { ArrowUploadRegular, CalligraphyPenRegular, DismissRegular } from "@fluentui/react-icons";
3
+ import { ImageFileTypes, debounce, isElement, isFunction, isNullOrEmptyArray, isNullOrEmptyString } from "@kwiz/common";
4
+ import * as React from "react";
5
+ import { useStateEX } from "../../helpers/hooks";
6
+ import { ButtonEX } from "../button";
7
+ import { ColorPickerEx } from "../ColorPickerDialog";
8
+ import { FileUpload } from "../file-upload";
9
+ import { Horizontal } from "../horizontal";
10
+ import { Vertical } from "../vertical";
11
+ import DrawPadManager from "./DrawPadManager";
12
+
13
+ interface iProps {
14
+ BackgroundColor?: string;
15
+ BorderColor?: string;
16
+ LineColor?: string;
17
+ Width?: number;
18
+ Height?: number;
19
+ Value?: string;
20
+ OnChange?: (newValue: string) => void;
21
+ ReadOnly?: boolean;
22
+ HideButtons?: boolean;
23
+ SignAsText?: string;
24
+ }
25
+ export const DrawPad: React.FunctionComponent<iProps> = (props) => {
26
+ const [LineColor, setLineColor] = useStateEX<string>(props.LineColor || tokens.colorBrandForeground1);
27
+ const [manager, setmanager] = useStateEX<DrawPadManager>(null);
28
+ const [canUndo, setcanUndo] = useStateEX<boolean>(false);
29
+ const [loadedFontNames, setloadedFontNames] = useStateEX<string[]>([]);
30
+
31
+ const canvasArea: React.RefObject<HTMLCanvasElement> = React.useRef();
32
+ const containerEle = React.useRef<HTMLDivElement>();
33
+
34
+ //load font for sign as text
35
+ React.useEffect(() => {
36
+ if (props.SignAsText && isNullOrEmptyArray(loadedFontNames)) {
37
+ let DancingScriptFont = new FontFace(
38
+ "Dancing Script",
39
+ "url(https://fonts.gstatic.com/s/dancingscript/v25/If2RXTr6YS-zF4S-kcSWSVi_szLgiuE.woff2) format('woff2')",
40
+ {
41
+ style: "normal",
42
+ weight: "400 700",
43
+ display: "swap",
44
+ 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"
45
+ }
46
+ );
47
+
48
+ DancingScriptFont.load().then(async loadedFont => {
49
+ document.fonts.add(loadedFont);
50
+ await document.fonts.ready;
51
+ setloadedFontNames(["Dancing Script"]);
52
+ });
53
+ }
54
+ }, [props.SignAsText, loadedFontNames]);
55
+
56
+ //setup manager
57
+ React.useEffect(() => {
58
+ if (!props.ReadOnly) {
59
+ //this gets called after each render...
60
+ if (!manager) {
61
+ if (canvasArea.current) {
62
+ let manager = new DrawPadManager(canvasArea.current);
63
+ setmanager(manager).then(() => UpdateCanvas());
64
+ if (isFunction(props.OnChange)) {
65
+ manager.addEventListener("endStroke", () => {
66
+ let value = "";
67
+ if (!manager.isEmpty()) {
68
+ value = manager.toDataURL("image/png");
69
+ }
70
+ if (!canUndo)
71
+ setcanUndo(true);
72
+
73
+ props.OnChange(value);
74
+ });
75
+ }
76
+ }
77
+ }
78
+ else {
79
+ UpdateCanvas();
80
+ }
81
+ }
82
+ });//run every time after render
83
+
84
+ //set value to canvas
85
+ const UpdateCanvas = React.useCallback(debounce(() => {
86
+ if (manager) {
87
+ manager.penColor.value = LineColor;
88
+
89
+ if (!isNullOrEmptyString(props.Value))
90
+ manager.fromDataURL(props.Value, { clear: true });
91
+ else
92
+ manager.clear();
93
+ }
94
+ }, 200, this), [manager, props.Value, LineColor]);
95
+
96
+ const onSignAs = React.useCallback(() => {
97
+ let canvas = canvasArea.current;
98
+ if (!isElement(canvas)) {
99
+ return;
100
+ }
101
+
102
+ let height = canvas.clientHeight;
103
+ let width = canvas.clientWidth;
104
+ let fontName = loadedFontNames[0];
105
+ let signAs = props.SignAsText;
106
+
107
+ let ctx = canvas.getContext("2d");
108
+ ctx.fillStyle = LineColor || LineColor || tokens.colorBrandForeground1;
109
+
110
+ let fontSize = 0.6 * height;
111
+ ctx.font = `${fontSize}px ${fontName}`;
112
+ let textMeasurement = ctx.measureText(signAs);
113
+ let textWidth = textMeasurement.width;
114
+ let maxWidth = 0.9 * width;
115
+
116
+ while (textWidth > maxWidth && fontSize > 1) {
117
+ fontSize = fontSize - 1;
118
+ ctx.font = `${fontSize}px ${fontName}`;
119
+ textMeasurement = ctx.measureText(signAs);
120
+ textWidth = textMeasurement.width;
121
+ }
122
+
123
+ let x = (width - textWidth) / 2;
124
+ let y = 0.6 * height; //baseline not starting point
125
+ ctx.fillText(signAs, x, y, width);
126
+ let url = canvas.toDataURL("image/png");
127
+ props.OnChange(url);
128
+ }, [canvasArea, props.OnChange]);
129
+
130
+
131
+
132
+ let width = props.Width > 0 ? props.Width : 400;
133
+ let height = props.Height > 0 ? props.Height : 200;
134
+
135
+ return <div ref={containerEle}><Horizontal>
136
+ <div style={{
137
+ position: "relative",
138
+ width: width,
139
+ height: height,
140
+ backgroundColor: props.BackgroundColor,
141
+ border: `1px solid ${props.BorderColor || tokens.colorNeutralStroke1}`
142
+ }}>
143
+ {props.ReadOnly
144
+ ? <img src={props.Value} style={{ position: "absolute", left: 0, top: 0, width: width, height: height }} />
145
+ :
146
+ <div style={{ position: "absolute", left: 0, top: 0, width: width, height: height }}>
147
+ <canvas
148
+ ref={canvasArea}
149
+ style={{
150
+ touchAction: "none",
151
+ userSelect: "none",
152
+ position: "absolute",
153
+ left: 0,
154
+ top: 0,
155
+ width: width,
156
+ height: height,
157
+ border: tokens.colorBrandStroke1
158
+ }} />
159
+ {isNullOrEmptyString(props.Value)
160
+ && !isNullOrEmptyString(props.SignAsText)
161
+ && !isNullOrEmptyArray(loadedFontNames)
162
+ && <ButtonEX
163
+ style={{
164
+ position: "absolute",
165
+ bottom: 0,
166
+ border: 0,
167
+ margin: 0,
168
+ right: 0,
169
+ height: 16
170
+ }}
171
+ icon={<CalligraphyPenRegular />}
172
+ title={`Sign as ${props.SignAsText}`}
173
+ onClick={() => {
174
+ onSignAs();
175
+ }}
176
+ />}
177
+ </div>
178
+ }
179
+ </div>
180
+ {!props.ReadOnly && !props.HideButtons && <Vertical nogap>
181
+ <ColorPickerEx buttonOnly value={props.LineColor} onChange={newColor => {
182
+ setLineColor(newColor);
183
+ }} />
184
+ <ButtonEX disabled={isNullOrEmptyString(props.Value)} title="Clear" icon={<DismissRegular />} onClick={() => {
185
+ //can call clear on the canvas, or can call the onchange which will cause a re-draw
186
+ props.OnChange("");
187
+ }} />
188
+ <FileUpload title="Load background image" icon={<ArrowUploadRegular />} limitFileTypes={ImageFileTypes} asBase64={base64 => {
189
+ props.OnChange(base64[0].base64);//this will trigger a change and state update
190
+ //self.state.manager.fromDataURL(base64);//this will just set the image to the canvas but won't trigger a change
191
+ }} />
192
+ </Vertical>}
193
+ </Horizontal>
194
+ </div>;
195
+ }