@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.
- package/LICENSE +21 -21
- package/README.md +53 -53
- package/dist/controls/svg.js +21 -21
- package/dist/controls/svg.js.map +1 -1
- package/dist/helpers/hooks.d.ts +1 -1
- package/dist/helpers/hooks.js +43 -30
- package/dist/helpers/hooks.js.map +1 -1
- package/package.json +85 -85
- package/.dependency-cruiser.js +0 -399
- package/.github/workflows/npm-publish.yml +0 -24
- package/dist/@types/forwardRef.d.ts +0 -0
- package/dist/@types/forwardRef.js +0 -1
- package/dist/@types/forwardRef.js.map +0 -1
- package/dist/controls/error-boundary copy.d.ts +0 -23
- package/dist/controls/error-boundary copy.js +0 -33
- package/dist/controls/error-boundary copy.js.map +0 -1
- package/dist/helpers/common.d.ts +0 -4
- package/dist/helpers/common.js +0 -2
- package/dist/helpers/common.js.map +0 -1
- package/dist/helpers/context.d.ts +0 -26
- package/dist/helpers/context.js +0 -15
- package/dist/helpers/context.js.map +0 -1
- package/dist/helpers/drag-drop/exports.d.ts +0 -12
- package/dist/helpers/drag-drop/exports.js +0 -3
- package/dist/helpers/drag-drop/exports.js.map +0 -1
- package/dist/helpers/exports.d.ts +0 -7
- package/dist/helpers/exports.js +0 -8
- package/dist/helpers/exports.js.map +0 -1
- package/src/_modules/config.ts +0 -9
- package/src/_modules/constants.ts +0 -3
- package/src/controls/ColorPickerDialog.tsx +0 -84
- package/src/controls/accordion.tsx +0 -62
- package/src/controls/button.tsx +0 -181
- package/src/controls/canvas/CustomEventTargetBase.ts +0 -33
- package/src/controls/canvas/DrawPad.tsx +0 -297
- package/src/controls/canvas/DrawPadManager.ts +0 -695
- package/src/controls/canvas/bezier.ts +0 -110
- package/src/controls/canvas/point.ts +0 -45
- package/src/controls/card-list.tsx +0 -32
- package/src/controls/card.tsx +0 -78
- package/src/controls/centered.tsx +0 -15
- package/src/controls/date.tsx +0 -88
- package/src/controls/diagram-picker.tsx +0 -97
- package/src/controls/divider.tsx +0 -16
- package/src/controls/dropdown.tsx +0 -67
- package/src/controls/error-boundary.tsx +0 -42
- package/src/controls/field-editor.tsx +0 -43
- package/src/controls/file-upload.tsx +0 -156
- package/src/controls/horizontal.tsx +0 -49
- package/src/controls/html-editor/editor.tsx +0 -182
- package/src/controls/index.ts +0 -33
- package/src/controls/input.tsx +0 -161
- package/src/controls/kwizoverflow.tsx +0 -107
- package/src/controls/list.tsx +0 -120
- package/src/controls/loading.tsx +0 -11
- package/src/controls/menu.tsx +0 -196
- package/src/controls/merge-text.tsx +0 -126
- package/src/controls/please-wait.tsx +0 -33
- package/src/controls/progress-bar.tsx +0 -110
- package/src/controls/prompt.tsx +0 -122
- package/src/controls/qrcode.tsx +0 -37
- package/src/controls/search.tsx +0 -72
- package/src/controls/section.tsx +0 -134
- package/src/controls/svg.tsx +0 -139
- package/src/controls/toolbar.tsx +0 -47
- package/src/controls/vertical-content.tsx +0 -50
- package/src/controls/vertical.tsx +0 -43
- package/src/helpers/block-nav.tsx +0 -89
- package/src/helpers/context-const.ts +0 -30
- package/src/helpers/context-export.tsx +0 -78
- package/src/helpers/context-internal.ts +0 -14
- package/src/helpers/drag-drop/drag-drop-container.tsx +0 -54
- package/src/helpers/drag-drop/drag-drop-context-internal.tsx +0 -10
- package/src/helpers/drag-drop/drag-drop-context.tsx +0 -62
- package/src/helpers/drag-drop/drag-drop.types.ts +0 -21
- package/src/helpers/drag-drop/index.ts +0 -12
- package/src/helpers/drag-drop/readme.md +0 -76
- package/src/helpers/drag-drop/use-draggable.ts +0 -48
- package/src/helpers/drag-drop/use-droppable.ts +0 -39
- package/src/helpers/forwardRef.ts +0 -7
- package/src/helpers/hooks-events.ts +0 -150
- package/src/helpers/hooks.tsx +0 -163
- package/src/helpers/index.ts +0 -8
- package/src/helpers/use-alerts.tsx +0 -75
- package/src/helpers/use-editable-control.tsx +0 -38
- package/src/helpers/use-toast.tsx +0 -30
- package/src/index.ts +0 -3
- package/src/styles/index.ts +0 -1
- package/src/styles/styles.ts +0 -105
- package/src/styles/theme.ts +0 -91
@@ -1,156 +0,0 @@
|
|
1
|
-
import { makeStyles, shorthands, tokens } from "@fluentui/react-components";
|
2
|
-
import { ArrowUploadRegular } from "@fluentui/react-icons";
|
3
|
-
import { isFunction, isNotEmptyArray, isNotEmptyString, isNullOrEmptyString, lastOrNull } from '@kwiz/common';
|
4
|
-
import * as React from "react";
|
5
|
-
import { dropFiles, useDragDropContext, useEffectOnlyOnMount } from "../helpers";
|
6
|
-
import { ButtonEX, ButtonEXProps, CompoundButtonEXSecondary } from "./button";
|
7
|
-
|
8
|
-
const useStyles = makeStyles({
|
9
|
-
addRowIsOver: {
|
10
|
-
...shorthands.borderColor(tokens.colorBrandBackground)
|
11
|
-
}
|
12
|
-
});
|
13
|
-
|
14
|
-
type base64Result = { base64: string, filename: string };
|
15
|
-
interface iProps {
|
16
|
-
showTitleWithIcon?: boolean;
|
17
|
-
title?: string;
|
18
|
-
/** Passing this will turn the button into a compound button */
|
19
|
-
secondaryContent?: string;
|
20
|
-
limitFileTypes?: string[];
|
21
|
-
allowMultiple?: boolean;
|
22
|
-
icon?: JSX.Element;
|
23
|
-
onChange?: (newFile: File | File[], errors: string[]) => void;
|
24
|
-
asBase64?: (files: base64Result[], errors: string[]) => void;
|
25
|
-
buttonProps?: Partial<ButtonEXProps>;
|
26
|
-
disabled?: boolean;
|
27
|
-
/** limit file size in MB, for the asBase64 */
|
28
|
-
fileSizeLimit?: number;
|
29
|
-
}
|
30
|
-
|
31
|
-
export const FileUpload = React.forwardRef<HTMLButtonElement, (iProps)>((props, ref) => {
|
32
|
-
const classes = useStyles();
|
33
|
-
const hiddenFileInput = React.useRef(null);
|
34
|
-
const onChangeRef = React.useRef(props.onChange);
|
35
|
-
const asBase64Ref = React.useRef(props.asBase64);
|
36
|
-
|
37
|
-
const isMulti = props.allowMultiple === true;
|
38
|
-
const icon = props.icon || <ArrowUploadRegular />;
|
39
|
-
const title = isNotEmptyString(props.title) ? props.title : `Drop or select ${isMulti ? 'files' : 'file'}`;
|
40
|
-
|
41
|
-
//keep onChange up to date
|
42
|
-
React.useEffect(() => {
|
43
|
-
onChangeRef.current = props.onChange;
|
44
|
-
}, [props.onChange]);
|
45
|
-
//keep onChange up to date
|
46
|
-
React.useEffect(() => {
|
47
|
-
asBase64Ref.current = props.asBase64;
|
48
|
-
}, [props.asBase64]);
|
49
|
-
|
50
|
-
|
51
|
-
const onGotFiles = React.useCallback(async (rawFiles: FileList) => {
|
52
|
-
let errors: string[] = [];
|
53
|
-
let acceptedFiles: File[] = [];
|
54
|
-
if (rawFiles && rawFiles.length > 0) {
|
55
|
-
//filter by types and size
|
56
|
-
for (let i = 0; i < (isMulti ? rawFiles.length : 1); i++) {
|
57
|
-
const currentFile = rawFiles[i];
|
58
|
-
let hadError = false;
|
59
|
-
if (props.fileSizeLimit > 0) {
|
60
|
-
const megabytes = currentFile.size / (1024 * 1024);
|
61
|
-
if (megabytes > props.fileSizeLimit) {
|
62
|
-
errors.push(`File ${currentFile.name} is over the size limit`);
|
63
|
-
hadError = true;
|
64
|
-
}
|
65
|
-
}
|
66
|
-
if (!hadError) {
|
67
|
-
if (isNotEmptyArray(props.limitFileTypes)) {
|
68
|
-
let fileType = lastOrNull(currentFile.name.split('.')).toLowerCase();
|
69
|
-
if (props.limitFileTypes.indexOf(fileType) < 0) {
|
70
|
-
errors.push(`File ${currentFile.name} is not allowed`);
|
71
|
-
hadError = true;
|
72
|
-
}
|
73
|
-
}
|
74
|
-
}
|
75
|
-
if (!hadError) acceptedFiles.push(currentFile);
|
76
|
-
}
|
77
|
-
}
|
78
|
-
|
79
|
-
if (isMulti) {
|
80
|
-
onChangeRef.current?.(acceptedFiles, errors);
|
81
|
-
}
|
82
|
-
else {
|
83
|
-
const fileUploaded = acceptedFiles[0];
|
84
|
-
onChangeRef.current?.(fileUploaded, errors);
|
85
|
-
}
|
86
|
-
|
87
|
-
if (isFunction(asBase64Ref.current)) {
|
88
|
-
const filesAs64: base64Result[] = [];
|
89
|
-
for (let i = 0; i < (isMulti ? acceptedFiles.length : 1); i++) {
|
90
|
-
const currentFile = acceptedFiles[i];
|
91
|
-
let hadError = false;
|
92
|
-
if (props.fileSizeLimit > 0) {
|
93
|
-
const megabytes = currentFile.size / (1024 * 1024);
|
94
|
-
if (megabytes > props.fileSizeLimit) {
|
95
|
-
errors.push(`File ${currentFile.name} is over the size limit`);
|
96
|
-
hadError = true;
|
97
|
-
}
|
98
|
-
}
|
99
|
-
if (!hadError) {
|
100
|
-
let as64 = await getFileAsBase64(acceptedFiles[i]);
|
101
|
-
if (as64) filesAs64.push(as64);
|
102
|
-
else errors.push(`Could not read file ${acceptedFiles[i].name}`);
|
103
|
-
}
|
104
|
-
}
|
105
|
-
asBase64Ref.current?.(filesAs64, errors);
|
106
|
-
}
|
107
|
-
}, useEffectOnlyOnMount);
|
108
|
-
|
109
|
-
const dropContext = useDragDropContext<never, dropFiles>({
|
110
|
-
dropInfo: {
|
111
|
-
acceptTypes: ["__NATIVE_FILE__"],
|
112
|
-
onItemDrop: item => {
|
113
|
-
onGotFiles(item.files);
|
114
|
-
}
|
115
|
-
}
|
116
|
-
});
|
117
|
-
|
118
|
-
return <>
|
119
|
-
{isNullOrEmptyString(props.secondaryContent)
|
120
|
-
? <ButtonEX ref={ref || dropContext.dragDropRef} {...(props.buttonProps || {})} icon={icon} showTitleWithIcon={props.showTitleWithIcon} onClick={() => {
|
121
|
-
hiddenFileInput.current.value = "";
|
122
|
-
hiddenFileInput.current.click();
|
123
|
-
}}
|
124
|
-
title={title} disabled={props.disabled}
|
125
|
-
className={dropContext.drop.isOver && classes.addRowIsOver}
|
126
|
-
/>
|
127
|
-
: <CompoundButtonEXSecondary ref={ref || dropContext.dragDropRef} {...(props.buttonProps || {})} icon={icon}
|
128
|
-
secondaryContent={props.secondaryContent}
|
129
|
-
onClick={() => {
|
130
|
-
hiddenFileInput.current.value = "";
|
131
|
-
hiddenFileInput.current.click();
|
132
|
-
}}
|
133
|
-
title={title} disabled={props.disabled}
|
134
|
-
className={dropContext.drop.isOver && classes.addRowIsOver}
|
135
|
-
/>}
|
136
|
-
<input type="file" ref={hiddenFileInput} style={{ display: "none" }} multiple={isMulti}
|
137
|
-
accept={isNotEmptyArray(props.limitFileTypes) ? props.limitFileTypes.map(ft => `.${ft}`).join() : undefined}
|
138
|
-
onChange={async (e) => onGotFiles(e.target.files)}
|
139
|
-
/>
|
140
|
-
</>;
|
141
|
-
});
|
142
|
-
|
143
|
-
async function getFileAsBase64(file: File): Promise<base64Result> {
|
144
|
-
return new Promise<base64Result>(resolve => {
|
145
|
-
const reader = new FileReader();
|
146
|
-
reader.onloadend = () => {
|
147
|
-
if (!isNullOrEmptyString(reader.result))
|
148
|
-
resolve({ filename: file.name, base64: reader.result as string });
|
149
|
-
else {
|
150
|
-
console.warn("Empty file selected");
|
151
|
-
resolve(null);
|
152
|
-
}
|
153
|
-
};
|
154
|
-
reader.readAsDataURL(file);
|
155
|
-
});
|
156
|
-
}
|
@@ -1,49 +0,0 @@
|
|
1
|
-
import { makeStyles } from '@fluentui/react-components';
|
2
|
-
import { isNotEmptyArray } from '@kwiz/common';
|
3
|
-
import React from 'react';
|
4
|
-
import { KnownClassNames, mixins } from '../styles/styles';
|
5
|
-
import { ISectionProps, Section } from './section';
|
6
|
-
|
7
|
-
const useStyles = makeStyles({
|
8
|
-
horizontal: {
|
9
|
-
...mixins.flex,
|
10
|
-
flexDirection: 'row'
|
11
|
-
},
|
12
|
-
wrap: mixins.wrap,
|
13
|
-
nogap: mixins.nogap,
|
14
|
-
centered: {
|
15
|
-
alignItems: "center"
|
16
|
-
},
|
17
|
-
hCentered: {
|
18
|
-
justifyContent: "center"
|
19
|
-
},
|
20
|
-
})
|
21
|
-
|
22
|
-
interface IProps extends ISectionProps {
|
23
|
-
wrap?: boolean;
|
24
|
-
nogap?: boolean;
|
25
|
-
/** vertical align center */
|
26
|
-
centered?: boolean;
|
27
|
-
/** horizontal centered */
|
28
|
-
hCentered?: boolean;
|
29
|
-
}
|
30
|
-
export const Horizontal = React.forwardRef<HTMLDivElement, React.PropsWithChildren<IProps>>((props, ref) => {
|
31
|
-
const cssNames = useStyles();
|
32
|
-
let css: string[] = [KnownClassNames.horizontal];
|
33
|
-
|
34
|
-
css.push(cssNames.horizontal);
|
35
|
-
if (props.wrap)
|
36
|
-
css.push(cssNames.wrap);
|
37
|
-
if (props.nogap)
|
38
|
-
css.push(cssNames.nogap);
|
39
|
-
if (props.centered)
|
40
|
-
css.push(cssNames.centered);
|
41
|
-
if (props.hCentered)
|
42
|
-
css.push(cssNames.hCentered);
|
43
|
-
|
44
|
-
if (isNotEmptyArray(props.css)) css.push(...props.css);
|
45
|
-
|
46
|
-
return (
|
47
|
-
<Section {...props} css={css} ref={ref} />
|
48
|
-
);
|
49
|
-
});
|
@@ -1,182 +0,0 @@
|
|
1
|
-
import { makeStyles, tokens } from '@fluentui/react-components';
|
2
|
-
import { ArrowMaximize16Regular, ArrowMinimize16Regular, Dismiss16Regular, Save16Regular } from '@fluentui/react-icons';
|
3
|
-
import { isObject } from '@kwiz/common';
|
4
|
-
import JoditEditor, { Jodit } from "jodit-react";
|
5
|
-
import React, { useRef } from 'react';
|
6
|
-
import { ButtonEX, ButtonEXProps } from '../button';
|
7
|
-
import { Section } from '../section';
|
8
|
-
import { IconToSVG } from '../svg';
|
9
|
-
|
10
|
-
//const logger = GetLogger("html-editor");
|
11
|
-
|
12
|
-
const useStyles = makeStyles({
|
13
|
-
htmlDiv: {
|
14
|
-
border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStrokeAccessible}`,
|
15
|
-
padding: tokens.spacingHorizontalS,
|
16
|
-
minHeight: `100px`
|
17
|
-
}
|
18
|
-
});
|
19
|
-
|
20
|
-
interface IProps {
|
21
|
-
value?: string;
|
22
|
-
/** get notified as soon as user is out of the control */
|
23
|
-
onChange?: (value: string) => void;
|
24
|
-
/** add a save button and get the value once the user clicks save, close the editor after */
|
25
|
-
onSave?: (value: string) => void;
|
26
|
-
/** add a cancel button to close this editor */
|
27
|
-
onCancel?: () => void;
|
28
|
-
readonly?: boolean;
|
29
|
-
css?: string[];
|
30
|
-
/** render a smaller toolbar */
|
31
|
-
smallToolbar?: boolean;
|
32
|
-
/** show a div, click on it to render the editor */
|
33
|
-
editOnDemand?: boolean;
|
34
|
-
|
35
|
-
/** all designers */
|
36
|
-
kitchensink?: boolean;
|
37
|
-
speech?: boolean;
|
38
|
-
spellcheck?: boolean;
|
39
|
-
table?: boolean;
|
40
|
-
media?: boolean;
|
41
|
-
source?: boolean;
|
42
|
-
/**
|
43
|
-
* true: render the control in full screen
|
44
|
-
* button: render only a button that opens the editor in full screen
|
45
|
-
*/
|
46
|
-
fullScreen?: boolean | ButtonEXProps;
|
47
|
-
}
|
48
|
-
type JoditExpanded = Jodit & {//& IViewBased<IViewOptions>
|
49
|
-
//value: string;
|
50
|
-
kwizInstance: {
|
51
|
-
props: IProps,
|
52
|
-
setShowFullScreen: (value: boolean) => void;
|
53
|
-
}
|
54
|
-
};
|
55
|
-
|
56
|
-
const saveIcon = IconToSVG(<Save16Regular title='Save' />);
|
57
|
-
const cancelIcon = IconToSVG(<Dismiss16Regular title='Cancel' />);
|
58
|
-
const maxIcon = IconToSVG(<ArrowMaximize16Regular title='Maximize' />);
|
59
|
-
const minIcon = IconToSVG(<ArrowMinimize16Regular title='Minimize' />);
|
60
|
-
export const HtmlEditor: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
61
|
-
const classes = useStyles();
|
62
|
-
const [active, setActive] = React.useState(false);
|
63
|
-
const [showFullScreen, setShowFullScreen] = React.useState(false);
|
64
|
-
|
65
|
-
|
66
|
-
//quill react demos: https://codesandbox.io/examples/package/react-quill
|
67
|
-
const editorRef = useRef<JoditExpanded>(null);
|
68
|
-
|
69
|
-
const extraConfig = {
|
70
|
-
uploader: {
|
71
|
-
insertImageAsBase64URI: true,
|
72
|
-
toolbarStickyOffset: 30,
|
73
|
-
},
|
74
|
-
autofocus: props.editOnDemand || showFullScreen,
|
75
|
-
};
|
76
|
-
|
77
|
-
const fullScreenButton = isObject(props.fullScreen) ? props.fullScreen as ButtonEXProps : null;
|
78
|
-
|
79
|
-
const options = props.kitchensink ? {
|
80
|
-
speech: true,
|
81
|
-
spellcheck: true,
|
82
|
-
table: true,
|
83
|
-
media: true,
|
84
|
-
source: true,
|
85
|
-
fullScreen: true
|
86
|
-
} : props
|
87
|
-
|
88
|
-
|
89
|
-
Jodit.defaultOptions.controls.save = {
|
90
|
-
template: () => saveIcon,
|
91
|
-
exec: (view: JoditExpanded) => {
|
92
|
-
view.kwizInstance.props.onChange?.(view.value);
|
93
|
-
view.kwizInstance.props.onSave?.(view.value);
|
94
|
-
view.kwizInstance.setShowFullScreen(false);
|
95
|
-
}
|
96
|
-
};
|
97
|
-
Jodit.defaultOptions.controls.cancel = {
|
98
|
-
template: () => cancelIcon,
|
99
|
-
exec: (view: JoditExpanded) => {
|
100
|
-
view.kwizInstance.props.onCancel?.();
|
101
|
-
view.kwizInstance.setShowFullScreen(false);
|
102
|
-
}
|
103
|
-
};
|
104
|
-
Jodit.defaultOptions.controls.maximize = {
|
105
|
-
template: () => maxIcon,
|
106
|
-
exec: (view: JoditExpanded) => {
|
107
|
-
view.kwizInstance.props.onChange?.(view.value);//pass value from smaller editor to bigger one
|
108
|
-
view.kwizInstance.setShowFullScreen(true);
|
109
|
-
}
|
110
|
-
};
|
111
|
-
Jodit.defaultOptions.controls.minimize = {
|
112
|
-
template: () => minIcon,
|
113
|
-
exec: (view: JoditExpanded) => {
|
114
|
-
view.kwizInstance.props.onChange?.(view.value);
|
115
|
-
view.kwizInstance.setShowFullScreen(false);
|
116
|
-
}
|
117
|
-
};
|
118
|
-
|
119
|
-
const minimalToolbar = !showFullScreen && options.fullScreen;
|
120
|
-
const buttons = (minimalToolbar
|
121
|
-
? `${props.onSave && 'save,'}${props.onCancel && 'cancel,'}maximize,bold,ul,ol`//inline, with full screen option - show very minimal toolbar
|
122
|
-
: `${props.onSave && 'save,'}${props.onCancel && 'cancel,'}${options.fullScreen && (showFullScreen
|
123
|
-
? fullScreenButton ? 'save,' : 'minimize,'//when in full screen, if we don't have a small editor but just a button - change the minimize button to save&close button
|
124
|
-
: 'maximize,')}${(props.onSave || props.onCancel || options.fullScreen) && '|,'}bold,italic,underline,strikethrough,|,ul,ol,font,fontsize,paragraph,lineHeight,superscript,subscript,copyformat,brush,eraser,|,${options.media && 'image,video,'}${options.spellcheck && 'spellcheck,'}${options.speech && 'speechRecognize,'}hr,${options.table && 'table,'}link,indent,outdent,${options.source && '---,source,'}`)
|
125
|
-
.replace(/undefined/g, '');
|
126
|
-
const removeButtons: string[] = minimalToolbar
|
127
|
-
//some buttons must be explicitly removed
|
128
|
-
? ["strikethrough", "italic", "eraser", "font", "fontsize", "paragraph", "lineHeight", "brush", "underline"]
|
129
|
-
: undefined;
|
130
|
-
|
131
|
-
const config = {
|
132
|
-
...extraConfig,
|
133
|
-
readonly: props.readonly,
|
134
|
-
inline: true,
|
135
|
-
statusbar: false,
|
136
|
-
toolbarButtonSize: props.smallToolbar ? "xsmall" : "middle",
|
137
|
-
buttons, removeButtons,
|
138
|
-
toolbarAdaptive: !minimalToolbar,
|
139
|
-
// events: {
|
140
|
-
// afterInit: (view: IViewBasedExpanded) => {
|
141
|
-
// view.kwizInstance = {
|
142
|
-
// props: props,
|
143
|
-
// setShowFullScreen: setShowFullScreen
|
144
|
-
// };
|
145
|
-
// }
|
146
|
-
// }
|
147
|
-
};
|
148
|
-
const editorElement = <JoditEditor key={"the-editor"}
|
149
|
-
{...({
|
150
|
-
editorRef: (view: JoditExpanded) => {
|
151
|
-
//expand it for toolbar buttons
|
152
|
-
view.kwizInstance = {
|
153
|
-
props: props,
|
154
|
-
setShowFullScreen: setShowFullScreen
|
155
|
-
};
|
156
|
-
editorRef.current = view;
|
157
|
-
}
|
158
|
-
})}
|
159
|
-
value={props.value || ""}
|
160
|
-
config={{ ...config as any }}
|
161
|
-
onBlur={newContent => {
|
162
|
-
props.onChange?.(newContent);
|
163
|
-
setActive(false);
|
164
|
-
}} // preferred to use only this option to update the content for performance reasons
|
165
|
-
//onChange={newContent => { }}
|
166
|
-
/>;
|
167
|
-
|
168
|
-
return (showFullScreen
|
169
|
-
? <Section fullscreen="portal">
|
170
|
-
{editorElement}
|
171
|
-
</Section>
|
172
|
-
: fullScreenButton
|
173
|
-
? <ButtonEX {...fullScreenButton} onClick={() => {
|
174
|
-
setShowFullScreen(true);
|
175
|
-
}} />
|
176
|
-
: props.editOnDemand && !active
|
177
|
-
? <div className={classes.htmlDiv} dangerouslySetInnerHTML={{ __html: props.value || "" }}
|
178
|
-
tabIndex={0} onFocus={() => setActive(true)}
|
179
|
-
onClick={() => setActive(true)} />
|
180
|
-
: editorElement
|
181
|
-
);
|
182
|
-
}
|
package/src/controls/index.ts
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
export * from './accordion';
|
2
|
-
export * from './button';
|
3
|
-
export * from './canvas/DrawPad';
|
4
|
-
export * from './card';
|
5
|
-
export * from './card-list';
|
6
|
-
export * from './centered';
|
7
|
-
export * from './ColorPickerDialog';
|
8
|
-
export * from './date';
|
9
|
-
export * from './diagram-picker';
|
10
|
-
export * from './divider';
|
11
|
-
export * from './dropdown';
|
12
|
-
export * from './error-boundary';
|
13
|
-
export * from './field-editor';
|
14
|
-
export * from './file-upload';
|
15
|
-
export * from './horizontal';
|
16
|
-
export * from './html-editor/editor';
|
17
|
-
export * from './input';
|
18
|
-
export * from './kwizoverflow';
|
19
|
-
export * from './list';
|
20
|
-
export * from './loading';
|
21
|
-
export * from './menu';
|
22
|
-
export * from './merge-text';
|
23
|
-
export * from './please-wait';
|
24
|
-
export * from './progress-bar';
|
25
|
-
export * from './prompt';
|
26
|
-
export * from './qrcode';
|
27
|
-
export * from './search';
|
28
|
-
export * from './section';
|
29
|
-
export * from './svg';
|
30
|
-
export * from './toolbar';
|
31
|
-
export * from './vertical';
|
32
|
-
export * from './vertical-content';
|
33
|
-
|
package/src/controls/input.tsx
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
import { GriffelStyle, Input, InputOnChangeData, InputProps, Label, Link, makeStyles, mergeClasses, Textarea, TextareaOnChangeData, TextareaProps } from '@fluentui/react-components';
|
2
|
-
import { isFunction, isNotEmptyArray, isNullOrEmptyString, isNullOrNaN, isNullOrUndefined, isNumber, pasteTextAtCursor, stopEvent } from '@kwiz/common';
|
3
|
-
import React, { useCallback, useEffect } from 'react';
|
4
|
-
import { useEffectOnlyOnMount, useRefWithState } from '../helpers';
|
5
|
-
import { useKWIZFluentContext } from '../helpers/context-internal';
|
6
|
-
import { useCommonStyles } from '../styles/styles';
|
7
|
-
import { Horizontal } from './horizontal';
|
8
|
-
import { MenuEx } from './menu';
|
9
|
-
import { Section } from './section';
|
10
|
-
import { Vertical } from './vertical';
|
11
|
-
|
12
|
-
|
13
|
-
interface IProps extends InputProps {
|
14
|
-
/** fire on enter */
|
15
|
-
onOK?: () => void;
|
16
|
-
/** fire on escape */
|
17
|
-
onCancel?: () => void;
|
18
|
-
tokens?: { title: string; value: string; replace?: boolean; }[];
|
19
|
-
tokenMenuLabel?: string;
|
20
|
-
}
|
21
|
-
export const InputEx: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
22
|
-
const ctx = useKWIZFluentContext();
|
23
|
-
const input = <Input appearance={ctx.inputAppearance} {...props}
|
24
|
-
onKeyDown={isFunction(props.onOK) || isFunction(props.onCancel)
|
25
|
-
? e => {
|
26
|
-
if (e.key === "Enter") props.onOK?.();
|
27
|
-
if (e.key === "Escape") props.onCancel?.();
|
28
|
-
}
|
29
|
-
: undefined
|
30
|
-
}
|
31
|
-
/>;
|
32
|
-
return (
|
33
|
-
isNotEmptyArray(props.tokens)
|
34
|
-
? <Vertical nogap>
|
35
|
-
{input}
|
36
|
-
<Horizontal nogap>
|
37
|
-
<Section main />
|
38
|
-
<MenuEx trigger={<Link>{props.tokenMenuLabel || "tokens"}</Link>} items={props.tokens.map(token =>
|
39
|
-
({
|
40
|
-
title: token.title, onClick: () => {
|
41
|
-
let newValue = props.value || "";
|
42
|
-
if (token.replace) {
|
43
|
-
newValue = token.value;
|
44
|
-
}
|
45
|
-
else {
|
46
|
-
if (isNullOrEmptyString(props.value))
|
47
|
-
newValue = token.value;
|
48
|
-
else
|
49
|
-
newValue += ` ${token.value}`;
|
50
|
-
}
|
51
|
-
props.onChange(null, {
|
52
|
-
value: newValue
|
53
|
-
});
|
54
|
-
}
|
55
|
-
}))} />
|
56
|
-
</Horizontal>
|
57
|
-
</Vertical>
|
58
|
-
: input
|
59
|
-
);
|
60
|
-
}
|
61
|
-
|
62
|
-
const fullSize: GriffelStyle = {
|
63
|
-
width: '100% !important',
|
64
|
-
maxHeight: '100% !important'
|
65
|
-
};
|
66
|
-
const useStyles = makeStyles({
|
67
|
-
fullSizeTextArea: {
|
68
|
-
...fullSize,
|
69
|
-
['& > textarea']: fullSize
|
70
|
-
},
|
71
|
-
})
|
72
|
-
|
73
|
-
interface IPropsTextArea extends TextareaProps {
|
74
|
-
fullSize?: boolean;
|
75
|
-
/** recalc the height to grow to show all text */
|
76
|
-
growNoShrink?: boolean;
|
77
|
-
allowTab?: boolean;
|
78
|
-
/** fire on enter */
|
79
|
-
onOK?: () => void;
|
80
|
-
/** fire on escape */
|
81
|
-
onCancel?: () => void;
|
82
|
-
onValueChange?: (e: React.ChangeEvent<HTMLTextAreaElement> | React.KeyboardEvent<HTMLTextAreaElement>, d: {
|
83
|
-
value: string;
|
84
|
-
elm: HTMLTextAreaElement;
|
85
|
-
}) => void;
|
86
|
-
}
|
87
|
-
export const TextAreaEx: React.FunctionComponent<React.PropsWithChildren<IPropsTextArea>> = (props) => {
|
88
|
-
const cssNames = useStyles();
|
89
|
-
let css: string[] = [];
|
90
|
-
|
91
|
-
if (props.fullSize) css.push(cssNames.fullSizeTextArea);
|
92
|
-
const textAreaRef = useRefWithState<HTMLTextAreaElement>(null);
|
93
|
-
const recalcHeight = React.useCallback(() => {
|
94
|
-
if (textAreaRef.ref.current && props.growNoShrink) {
|
95
|
-
if (textAreaRef.ref.current.scrollHeight > textAreaRef.ref.current.clientHeight)
|
96
|
-
textAreaRef.ref.current.style.minHeight = textAreaRef.ref.current.scrollHeight + 'px';
|
97
|
-
}
|
98
|
-
}, useEffectOnlyOnMount);
|
99
|
-
|
100
|
-
useEffect(() => { recalcHeight(); }, [textAreaRef.value]);
|
101
|
-
|
102
|
-
const onChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement> | React.KeyboardEvent<HTMLTextAreaElement>, d: TextareaOnChangeData) => {
|
103
|
-
props.onValueChange?.(e, { value: d.value, elm: textAreaRef.ref.current });
|
104
|
-
recalcHeight();
|
105
|
-
}, [props.onChange]);
|
106
|
-
|
107
|
-
const needOnKeyDown = props.allowTab || isFunction(props.onOK) || isFunction(props.onCancel);
|
108
|
-
|
109
|
-
let style: React.CSSProperties = { height: '100%', ...props.style };
|
110
|
-
|
111
|
-
return (
|
112
|
-
<Textarea ref={textAreaRef.set} className={mergeClasses(...css)} {...props} style={style}
|
113
|
-
onKeyDown={needOnKeyDown ? (e) => {
|
114
|
-
if (props.allowTab && e.key === "Tab") {
|
115
|
-
stopEvent(e);
|
116
|
-
const textArea = e.target as HTMLTextAreaElement;
|
117
|
-
pasteTextAtCursor(textArea, "\t");
|
118
|
-
onChange(e, { value: textArea.value });
|
119
|
-
return;
|
120
|
-
}
|
121
|
-
if (e.key === "Enter") props.onOK?.();
|
122
|
-
if (e.key === "Escape") props.onCancel?.();
|
123
|
-
props.onKeyDown?.(e);
|
124
|
-
} : props.onKeyDown}
|
125
|
-
onChange={(e, d) => {
|
126
|
-
props.onChange?.(e, d);
|
127
|
-
onChange(e, d);
|
128
|
-
}} />
|
129
|
-
);
|
130
|
-
}
|
131
|
-
|
132
|
-
|
133
|
-
interface INumberProps extends Omit<IProps, "value" | "onChange" | "defaultValue" | "inputMode"> {
|
134
|
-
defaultValue?: number;
|
135
|
-
onChange: (value: number) => void;
|
136
|
-
allowDecimals?: boolean;
|
137
|
-
/** if sent true - onChange will only be called when a valid non-empty value is being set */
|
138
|
-
required?: boolean;
|
139
|
-
}
|
140
|
-
export const InputNumberEx: React.FunctionComponent<React.PropsWithChildren<INumberProps>> = (props) => {
|
141
|
-
const commonStyles = useCommonStyles();
|
142
|
-
const [valueStr, setValueStr] = React.useState(isNumber(props.defaultValue) ? `${props.defaultValue}` : '');
|
143
|
-
const [isValid, setIsValid] = React.useState(true);
|
144
|
-
const onChange = React.useCallback((ev: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData) => {
|
145
|
-
const newValue = data.value;
|
146
|
-
setValueStr(newValue);//update text box anyways
|
147
|
-
const asNumber = props.allowDecimals ? parseFloat(newValue) : parseInt(newValue, 10);
|
148
|
-
const isValid = props.required ? !isNullOrNaN(asNumber) : isNullOrUndefined(asNumber) || !isNaN(asNumber);
|
149
|
-
setIsValid(isValid);
|
150
|
-
props.onChange(isValid ? asNumber : null);
|
151
|
-
}, [props.allowDecimals, props.onChange, props.required]);
|
152
|
-
|
153
|
-
const passProps: IProps = { ...props, defaultValue: undefined, value: undefined, onChange: undefined };
|
154
|
-
|
155
|
-
return (
|
156
|
-
<Vertical nogap>
|
157
|
-
<InputEx inputMode={props.allowDecimals ? "decimal" : "numeric"} {...passProps} value={valueStr} onChange={onChange} />
|
158
|
-
{!isValid && <Label className={commonStyles.validationLabel}>this is not a valid value</Label>}
|
159
|
-
</Vertical>
|
160
|
-
);
|
161
|
-
}
|
@@ -1,107 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
Menu, MenuButton, MenuItem, MenuList, MenuPopover, MenuTrigger, Overflow, OverflowItem,
|
3
|
-
useIsOverflowItemVisible, useOverflowMenu
|
4
|
-
} from "@fluentui/react-components";
|
5
|
-
import { MoreHorizontalFilled } from "@fluentui/react-icons";
|
6
|
-
import { isNumber } from '@kwiz/common';
|
7
|
-
import { useKWIZFluentContext } from "../helpers/context-internal";
|
8
|
-
|
9
|
-
interface IProps<ItemType> {
|
10
|
-
/** you cannot have a menu with trigger in overflow items. put those in groupWrapper controls before/after rendering children. */
|
11
|
-
items: ItemType[];
|
12
|
-
getKey: (item: ItemType, index: number) => string;
|
13
|
-
getPriority?: (item: ItemType, index: number) => number;
|
14
|
-
renderItem: (item: ItemType, index: number, overflow?: boolean) => JSX.Element;
|
15
|
-
groupWrapper?: (children: React.ReactNode) => JSX.Element;
|
16
|
-
menuRef?: React.RefObject<HTMLButtonElement>;
|
17
|
-
menuWrapper?: (children: React.ReactNode) => JSX.Element;
|
18
|
-
menuTrigger?: (ref: React.RefObject<HTMLButtonElement>, overflowCount: number) => JSX.Element;
|
19
|
-
className?: string;
|
20
|
-
}
|
21
|
-
const OverflowMenu = <ItemType,>(props: IProps<ItemType>) => {
|
22
|
-
const ctx = useKWIZFluentContext();
|
23
|
-
|
24
|
-
const { ref, isOverflowing, overflowCount } =
|
25
|
-
useOverflowMenu<HTMLButtonElement>();
|
26
|
-
|
27
|
-
if (!isOverflowing) {
|
28
|
-
return null;
|
29
|
-
}
|
30
|
-
|
31
|
-
let menu = <Menu mountNode={ctx.mountNode}>
|
32
|
-
<MenuTrigger disableButtonEnhancement>
|
33
|
-
{props.menuTrigger
|
34
|
-
? props.menuTrigger(props.menuRef || ref, overflowCount)
|
35
|
-
: <MenuButton
|
36
|
-
icon={<MoreHorizontalFilled />}
|
37
|
-
ref={props.menuRef || ref}
|
38
|
-
aria-label="More items"
|
39
|
-
appearance="subtle"
|
40
|
-
/>}
|
41
|
-
</MenuTrigger>
|
42
|
-
<MenuPopover>
|
43
|
-
<MenuList>
|
44
|
-
{props.items.map((item, index) => (
|
45
|
-
<OverflowMenuItem key={props.getKey(item, index)} {...props} item={item} index={index} />
|
46
|
-
))}
|
47
|
-
</MenuList>
|
48
|
-
</MenuPopover>
|
49
|
-
</Menu>;
|
50
|
-
|
51
|
-
return (
|
52
|
-
props.menuWrapper
|
53
|
-
? props.menuWrapper(menu)
|
54
|
-
: menu
|
55
|
-
);
|
56
|
-
}
|
57
|
-
|
58
|
-
const OverflowMenuItem = <ItemType,>(props: IProps<ItemType> & { item: ItemType, index: number }) => {
|
59
|
-
const isVisible = useIsOverflowItemVisible(props.getKey(props.item, props.index));
|
60
|
-
|
61
|
-
if (isVisible) {
|
62
|
-
return null;
|
63
|
-
}
|
64
|
-
|
65
|
-
return (
|
66
|
-
<MenuItem key={props.getKey(props.item, props.index)}>
|
67
|
-
{props.renderItem(props.item, props.index, true)}
|
68
|
-
</MenuItem>
|
69
|
-
);
|
70
|
-
};
|
71
|
-
export const KWIZOverflow = <ItemType,>(props: IProps<ItemType>) => {
|
72
|
-
let content: JSX.Element[] = [];
|
73
|
-
let addMenu = () => {
|
74
|
-
if (menuIndex >= 0)
|
75
|
-
content.splice(menuIndex, 0, <OverflowMenu key="overflow_menu" {...props} />);
|
76
|
-
else
|
77
|
-
content.push(<OverflowMenu key="overflow_menu" {...props} />);
|
78
|
-
};
|
79
|
-
|
80
|
-
let menuIndex = -1;
|
81
|
-
|
82
|
-
props.items.forEach((item, index) => {
|
83
|
-
//add the menu before the first item with priority
|
84
|
-
let priority = props.getPriority ? props.getPriority(item, index) : undefined;
|
85
|
-
if (isNumber(priority) && priority > 0)
|
86
|
-
menuIndex = index;
|
87
|
-
|
88
|
-
content.push(<OverflowItem key={props.getKey(item, index)} id={props.getKey(item, index)}
|
89
|
-
priority={priority}>
|
90
|
-
{props.renderItem(item, index)}
|
91
|
-
</OverflowItem>);
|
92
|
-
});
|
93
|
-
|
94
|
-
addMenu();
|
95
|
-
|
96
|
-
return (
|
97
|
-
<Overflow minimumVisible={2} padding={60} key={`overflow_${props.items.length}`}>
|
98
|
-
<div style={{ overflow: "hidden" }} className={props.className}>
|
99
|
-
{
|
100
|
-
props.groupWrapper
|
101
|
-
? props.groupWrapper(content)
|
102
|
-
: content
|
103
|
-
}
|
104
|
-
</div>
|
105
|
-
</Overflow>
|
106
|
-
)
|
107
|
-
};
|