@kwiz/fluentui 1.0.73 → 1.0.75
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/.github/workflows/npm-publish.yml +24 -24
- package/LICENSE +21 -21
- package/README.md +53 -53
- package/dist/@types/forwardRef.d.ts +0 -0
- package/dist/@types/forwardRef.js +1 -0
- package/dist/@types/forwardRef.js.map +1 -0
- package/dist/controls/error-boundary copy.d.ts +23 -0
- package/dist/controls/error-boundary copy.js +33 -0
- package/dist/controls/error-boundary copy.js.map +1 -0
- package/dist/controls/menu.js +2 -2
- package/dist/controls/menu.js.map +1 -1
- package/dist/controls/search.js +19 -11
- package/dist/controls/search.js.map +1 -1
- package/dist/controls/svg.js +21 -21
- package/dist/controls/svg.js.map +1 -1
- package/dist/helpers/common.d.ts +4 -0
- package/dist/helpers/common.js +2 -0
- package/dist/helpers/common.js.map +1 -0
- package/dist/helpers/context.d.ts +26 -0
- package/dist/helpers/context.js +15 -0
- package/dist/helpers/context.js.map +1 -0
- package/dist/helpers/drag-drop/exports.d.ts +12 -0
- package/dist/helpers/drag-drop/exports.js +3 -0
- package/dist/helpers/drag-drop/exports.js.map +1 -0
- package/dist/helpers/exports.d.ts +7 -0
- package/dist/helpers/exports.js +8 -0
- package/dist/helpers/exports.js.map +1 -0
- package/dist/helpers/use-editable-control.d.ts +1 -1
- package/dist/helpers/use-editable-control.js.map +1 -1
- package/package.json +85 -84
- package/src/_modules/config.ts +9 -9
- package/src/_modules/constants.ts +3 -3
- package/src/controls/ColorPickerDialog.tsx +83 -83
- package/src/controls/accordion.tsx +62 -62
- package/src/controls/button.tsx +180 -180
- package/src/controls/canvas/CustomEventTargetBase.ts +32 -32
- package/src/controls/canvas/DrawPad.tsx +296 -296
- package/src/controls/canvas/DrawPadManager.ts +694 -694
- package/src/controls/canvas/bezier.ts +109 -109
- package/src/controls/canvas/point.ts +44 -44
- package/src/controls/card-list.tsx +31 -31
- package/src/controls/card.tsx +77 -77
- package/src/controls/centered.tsx +14 -14
- package/src/controls/date.tsx +87 -87
- package/src/controls/diagram-picker.tsx +96 -96
- package/src/controls/divider.tsx +15 -15
- package/src/controls/dropdown.tsx +66 -66
- package/src/controls/error-boundary.tsx +41 -41
- package/src/controls/field-editor.tsx +42 -42
- package/src/controls/file-upload.tsx +155 -155
- package/src/controls/horizontal.tsx +48 -48
- package/src/controls/html-editor/editor.tsx +182 -182
- package/src/controls/index.ts +33 -33
- package/src/controls/input.tsx +160 -160
- package/src/controls/kwizoverflow.tsx +106 -106
- package/src/controls/list.tsx +119 -119
- package/src/controls/loading.tsx +10 -10
- package/src/controls/menu.tsx +173 -173
- package/src/controls/merge-text.tsx +126 -126
- package/src/controls/please-wait.tsx +32 -32
- package/src/controls/progress-bar.tsx +109 -109
- package/src/controls/prompt.tsx +121 -121
- package/src/controls/qrcode.tsx +36 -36
- package/src/controls/search.tsx +71 -61
- package/src/controls/section.tsx +133 -133
- package/src/controls/svg.tsx +138 -138
- package/src/controls/toolbar.tsx +46 -46
- package/src/controls/vertical-content.tsx +49 -49
- package/src/controls/vertical.tsx +42 -42
- package/src/helpers/block-nav.tsx +88 -88
- package/src/helpers/context-const.ts +29 -29
- package/src/helpers/context-export.tsx +77 -77
- package/src/helpers/context-internal.ts +13 -13
- package/src/helpers/drag-drop/drag-drop-container.tsx +53 -53
- package/src/helpers/drag-drop/drag-drop-context-internal.tsx +9 -9
- package/src/helpers/drag-drop/drag-drop-context.tsx +61 -61
- package/src/helpers/drag-drop/drag-drop.types.ts +21 -21
- package/src/helpers/drag-drop/index.ts +12 -12
- package/src/helpers/drag-drop/readme.md +75 -75
- package/src/helpers/drag-drop/use-draggable.ts +47 -47
- package/src/helpers/drag-drop/use-droppable.ts +38 -38
- package/src/helpers/forwardRef.ts +7 -7
- package/src/helpers/hooks-events.ts +149 -149
- package/src/helpers/hooks.tsx +141 -141
- package/src/helpers/index.ts +8 -8
- package/src/helpers/use-alerts.tsx +74 -74
- package/src/helpers/use-editable-control.tsx +37 -37
- package/src/helpers/use-toast.tsx +29 -29
- package/src/index.ts +2 -2
- package/src/styles/index.ts +1 -1
- package/src/styles/styles.ts +104 -104
- package/src/styles/theme.ts +90 -90
@@ -1,43 +1,43 @@
|
|
1
|
-
import { Field, mergeClasses } from '@fluentui/react-components';
|
2
|
-
import { isNullOrUndefined } from '@kwiz/common';
|
3
|
-
import React from 'react';
|
4
|
-
import { GetLogger } from '../_modules/config';
|
5
|
-
import { InputEx, TextAreaEx } from './input';
|
6
|
-
|
7
|
-
const logger = GetLogger('FieldEditor');
|
8
|
-
|
9
|
-
interface IProps {
|
10
|
-
required?: boolean;
|
11
|
-
error?: string;
|
12
|
-
value: string;
|
13
|
-
onChange: (newValue: string) => void;
|
14
|
-
css: string[];
|
15
|
-
label: string;
|
16
|
-
description?: string;
|
17
|
-
type?: "text" | "multiline";
|
18
|
-
allowTab?: boolean;
|
19
|
-
}
|
20
|
-
export const FieldEditor: React.FunctionComponent<IProps> = (props) => {
|
21
|
-
if (isNullOrUndefined(props.value)) {
|
22
|
-
logger.error(`${props.label}: value should not be null`);
|
23
|
-
}
|
24
|
-
return (
|
25
|
-
<Field required={props.required}
|
26
|
-
validationMessage={props.error || props.description}
|
27
|
-
validationState={props.error ? "error" : "none"}>
|
28
|
-
{props.type === "multiline"
|
29
|
-
? <TextAreaEx className={props.css && mergeClasses(...props.css)}
|
30
|
-
required={props.required}
|
31
|
-
placeholder={props.label}
|
32
|
-
value={props.value || ""}
|
33
|
-
allowTab={props.allowTab}
|
34
|
-
onValueChange={(e, data) => props.onChange(data.value)}
|
35
|
-
/>
|
36
|
-
: <InputEx className={props.css && mergeClasses(...props.css)}
|
37
|
-
required={props.required}
|
38
|
-
placeholder={props.label}
|
39
|
-
value={props.value || ""}
|
40
|
-
onChange={(e, data) => props.onChange(data.value)} />}
|
41
|
-
</Field>
|
42
|
-
);
|
1
|
+
import { Field, mergeClasses } from '@fluentui/react-components';
|
2
|
+
import { isNullOrUndefined } from '@kwiz/common';
|
3
|
+
import React from 'react';
|
4
|
+
import { GetLogger } from '../_modules/config';
|
5
|
+
import { InputEx, TextAreaEx } from './input';
|
6
|
+
|
7
|
+
const logger = GetLogger('FieldEditor');
|
8
|
+
|
9
|
+
interface IProps {
|
10
|
+
required?: boolean;
|
11
|
+
error?: string;
|
12
|
+
value: string;
|
13
|
+
onChange: (newValue: string) => void;
|
14
|
+
css: string[];
|
15
|
+
label: string;
|
16
|
+
description?: string;
|
17
|
+
type?: "text" | "multiline";
|
18
|
+
allowTab?: boolean;
|
19
|
+
}
|
20
|
+
export const FieldEditor: React.FunctionComponent<IProps> = (props) => {
|
21
|
+
if (isNullOrUndefined(props.value)) {
|
22
|
+
logger.error(`${props.label}: value should not be null`);
|
23
|
+
}
|
24
|
+
return (
|
25
|
+
<Field required={props.required}
|
26
|
+
validationMessage={props.error || props.description}
|
27
|
+
validationState={props.error ? "error" : "none"}>
|
28
|
+
{props.type === "multiline"
|
29
|
+
? <TextAreaEx className={props.css && mergeClasses(...props.css)}
|
30
|
+
required={props.required}
|
31
|
+
placeholder={props.label}
|
32
|
+
value={props.value || ""}
|
33
|
+
allowTab={props.allowTab}
|
34
|
+
onValueChange={(e, data) => props.onChange(data.value)}
|
35
|
+
/>
|
36
|
+
: <InputEx className={props.css && mergeClasses(...props.css)}
|
37
|
+
required={props.required}
|
38
|
+
placeholder={props.label}
|
39
|
+
value={props.value || ""}
|
40
|
+
onChange={(e, data) => props.onChange(data.value)} />}
|
41
|
+
</Field>
|
42
|
+
);
|
43
43
|
}
|
@@ -1,156 +1,156 @@
|
|
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
|
-
});
|
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
156
|
}
|
@@ -1,49 +1,49 @@
|
|
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} />
|
48
|
-
);
|
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} />
|
48
|
+
);
|
49
49
|
});
|