@kwiz/fluentui 1.0.40 → 1.0.41
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/dist/controls/ColorPickerDialog.d.ts +13 -0
- package/dist/controls/ColorPickerDialog.js +34 -0
- package/dist/controls/ColorPickerDialog.js.map +1 -0
- package/dist/controls/canvas/CustomEventTargetBase.d.ts +7 -0
- package/dist/controls/canvas/CustomEventTargetBase.js +22 -0
- package/dist/controls/canvas/CustomEventTargetBase.js.map +1 -0
- package/dist/controls/canvas/DrawPad.d.ts +15 -0
- package/dist/controls/canvas/DrawPad.js +151 -0
- package/dist/controls/canvas/DrawPad.js.map +1 -0
- package/dist/controls/canvas/DrawPadManager.d.ts +84 -0
- package/dist/controls/canvas/DrawPadManager.js +478 -0
- package/dist/controls/canvas/DrawPadManager.js.map +1 -0
- package/dist/controls/canvas/bezier.d.ts +17 -0
- package/dist/controls/canvas/bezier.js +65 -0
- package/dist/controls/canvas/bezier.js.map +1 -0
- package/dist/controls/canvas/point.d.ts +16 -0
- package/dist/controls/canvas/point.js +26 -0
- package/dist/controls/canvas/point.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/controls/ColorPickerDialog.tsx +76 -0
- package/src/controls/canvas/CustomEventTargetBase.ts +33 -0
- package/src/controls/canvas/DrawPad.tsx +195 -0
- package/src/controls/canvas/DrawPadManager.ts +668 -0
- package/src/controls/canvas/bezier.ts +110 -0
- package/src/controls/canvas/point.ts +45 -0
- package/src/index.ts +2 -0
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.
|
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.
|
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
|
+
}
|