@kwiz/fluentui 1.0.16 → 1.0.19
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 +34 -0
- package/LICENSE +21 -21
- package/README.md +26 -26
- package/package.json +72 -72
- package/src/_modules/config.ts +9 -9
- package/src/_modules/constants.ts +3 -3
- package/src/controls/accordion.tsx +48 -48
- package/src/controls/button.tsx +169 -169
- package/src/controls/centered.tsx +22 -22
- package/src/controls/date.tsx +39 -39
- package/src/controls/dropdown.tsx +51 -51
- package/src/controls/error-boundary.tsx +41 -41
- package/src/controls/field-editor.tsx +40 -40
- package/src/controls/file-upload.tsx +67 -67
- package/src/controls/horizontal.tsx +34 -34
- package/src/controls/input.tsx +60 -60
- package/src/controls/kwizoverflow.tsx +103 -103
- package/src/controls/list.tsx +117 -117
- package/src/controls/loading.tsx +10 -10
- package/src/controls/please-wait.tsx +32 -32
- package/src/controls/prompt.tsx +96 -96
- package/src/controls/search.tsx +65 -65
- package/src/controls/section.tsx +51 -51
- package/src/controls/svg.tsx +120 -120
- package/src/controls/toolbar.tsx +48 -48
- package/src/controls/vertical-content.tsx +49 -49
- package/src/controls/vertical.tsx +34 -34
- package/src/helpers/context.ts +39 -39
- package/src/helpers/hooks.tsx +335 -335
- package/src/index.ts +26 -26
- package/src/styles/styles.ts +87 -87
- package/src/styles/theme.ts +90 -90
- package/dist/_modules/build.d.ts +0 -2
- package/dist/_modules/build.js +0 -3
- package/dist/_modules/build.js.map +0 -1
- package/dist/_modules/config.d.ts +0 -1
- package/dist/_modules/config.js +0 -9
- package/dist/_modules/config.js.map +0 -1
- package/dist/_modules/constants.d.ts +0 -2
- package/dist/_modules/constants.js +0 -3
- package/dist/_modules/constants.js.map +0 -1
- package/dist/_modules/exports-index.d.ts +0 -1
- package/dist/_modules/exports-index.js +0 -2
- package/dist/_modules/exports-index.js.map +0 -1
- package/dist/controls/accordion.d.ts +0 -13
- package/dist/controls/accordion.js +0 -27
- package/dist/controls/accordion.js.map +0 -1
- package/dist/controls/button.d.ts +0 -28
- package/dist/controls/button.js +0 -113
- package/dist/controls/button.js.map +0 -1
- package/dist/controls/centered.d.ts +0 -5
- package/dist/controls/centered.js +0 -14
- package/dist/controls/centered.js.map +0 -1
- package/dist/controls/date.d.ts +0 -8
- package/dist/controls/date.js +0 -32
- package/dist/controls/date.js.map +0 -1
- package/dist/controls/dropdown.d.ts +0 -29
- package/dist/controls/dropdown.js +0 -27
- package/dist/controls/dropdown.js.map +0 -1
- package/dist/controls/error-boundary.d.ts +0 -23
- package/dist/controls/error-boundary.js +0 -33
- package/dist/controls/error-boundary.js.map +0 -1
- package/dist/controls/exports-index.d.ts +0 -17
- package/dist/controls/exports-index.js +0 -18
- package/dist/controls/exports-index.js.map +0 -1
- package/dist/controls/field-editor.d.ts +0 -13
- package/dist/controls/field-editor.js +0 -15
- package/dist/controls/field-editor.js.map +0 -1
- package/dist/controls/file-upload.d.ts +0 -18
- package/dist/controls/file-upload.js +0 -41
- package/dist/controls/file-upload.js.map +0 -1
- package/dist/controls/horizontal.d.ts +0 -8
- package/dist/controls/horizontal.js +0 -23
- package/dist/controls/horizontal.js.map +0 -1
- package/dist/controls/input.d.ts +0 -13
- package/dist/controls/input.js +0 -43
- package/dist/controls/input.js.map +0 -1
- package/dist/controls/kwizoverflow.d.ts +0 -14
- package/dist/controls/kwizoverflow.js +0 -45
- package/dist/controls/kwizoverflow.js.map +0 -1
- package/dist/controls/list.d.ts +0 -21
- package/dist/controls/list.js +0 -72
- package/dist/controls/list.js.map +0 -1
- package/dist/controls/loading copy.d.ts +0 -5
- package/dist/controls/loading copy.js +0 -7
- package/dist/controls/loading copy.js.map +0 -1
- package/dist/controls/loading.d.ts +0 -5
- package/dist/controls/loading.js +0 -7
- package/dist/controls/loading.js.map +0 -1
- package/dist/controls/please-wait.d.ts +0 -18
- package/dist/controls/please-wait.js +0 -16
- package/dist/controls/please-wait.js.map +0 -1
- package/dist/controls/prompt.d.ts +0 -32
- package/dist/controls/prompt.js +0 -31
- package/dist/controls/prompt.js.map +0 -1
- package/dist/controls/search.d.ts +0 -13
- package/dist/controls/search.js +0 -47
- package/dist/controls/search.js.map +0 -1
- package/dist/controls/section.d.ts +0 -14
- package/dist/controls/section.js +0 -27
- package/dist/controls/section.js.map +0 -1
- package/dist/controls/svg.d.ts +0 -23
- package/dist/controls/svg.js +0 -45
- package/dist/controls/svg.js.map +0 -1
- package/dist/controls/toolbar.d.ts +0 -12
- package/dist/controls/toolbar.js +0 -23
- package/dist/controls/toolbar.js.map +0 -1
- package/dist/controls/vertical-content.d.ts +0 -6
- package/dist/controls/vertical-content.js +0 -37
- package/dist/controls/vertical-content.js.map +0 -1
- package/dist/controls/vertical.d.ts +0 -8
- package/dist/controls/vertical.js +0 -23
- package/dist/controls/vertical.js.map +0 -1
- package/dist/exports-index.d.ts +0 -3
- package/dist/exports-index.js +0 -4
- package/dist/exports-index.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/hooks.d.ts +0 -62
- package/dist/helpers/hooks.js +0 -287
- package/dist/helpers/hooks.js.map +0 -1
- package/dist/index.d.ts +0 -25
- package/dist/index.js +0 -25
- package/dist/index.js.map +0 -1
- package/dist/styles/exports-index.d.ts +0 -2
- package/dist/styles/exports-index.js +0 -3
- package/dist/styles/exports-index.js.map +0 -1
- package/dist/styles/styles.d.ts +0 -19
- package/dist/styles/styles.js +0 -79
- package/dist/styles/styles.js.map +0 -1
- package/dist/styles/theme.d.ts +0 -6
- package/dist/styles/theme.js +0 -77
- package/dist/styles/theme.js.map +0 -1
package/src/controls/list.tsx
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import { makeStyles, tokens } from '@fluentui/react-components';
|
|
2
|
-
import { LOGO_BLUE_SQUARE, LOGO_WHITE_SQUARE, isNullOrUndefined, isString } from '@kwiz/common';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { KnownClassNames, mixins } from '../styles/styles';
|
|
5
|
-
import { Horizontal } from './horizontal';
|
|
6
|
-
import { Section } from './section';
|
|
7
|
-
import { Vertical } from './vertical';
|
|
8
|
-
|
|
9
|
-
const useStyles = makeStyles({
|
|
10
|
-
list: {
|
|
11
|
-
rowGap: 0
|
|
12
|
-
},
|
|
13
|
-
listItem: {
|
|
14
|
-
padding: tokens.spacingVerticalS,
|
|
15
|
-
':hover': {
|
|
16
|
-
backgroundColor: tokens.colorNeutralBackground1Hover
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
listItemSelected: {
|
|
20
|
-
backgroundColor: tokens.colorNeutralBackground1Selected
|
|
21
|
-
},
|
|
22
|
-
media: {
|
|
23
|
-
width: '32px',
|
|
24
|
-
fontSize: tokens.fontSizeBase600,
|
|
25
|
-
display: 'flex',
|
|
26
|
-
flexDirection: 'column',
|
|
27
|
-
justifyContent: 'center'
|
|
28
|
-
},
|
|
29
|
-
image: {
|
|
30
|
-
width: tokens.lineHeightBase600,
|
|
31
|
-
height: tokens.lineHeightBase600,
|
|
32
|
-
backgroundPosition: 'center center',
|
|
33
|
-
backgroundSize: 'cover',
|
|
34
|
-
borderRadius: tokens.borderRadiusCircular,
|
|
35
|
-
border: `1px solid ${tokens.colorNeutralStroke1}`
|
|
36
|
-
},
|
|
37
|
-
listItemBody: {
|
|
38
|
-
rowGap: 0,
|
|
39
|
-
width: 'calc(100% - 44px)'
|
|
40
|
-
},
|
|
41
|
-
listItemHeader: mixins.ellipsis,
|
|
42
|
-
listItemContent: {
|
|
43
|
-
...mixins.ellipsis,
|
|
44
|
-
fontSize: tokens.fontSizeBase200
|
|
45
|
-
},
|
|
46
|
-
listItemMedia: {
|
|
47
|
-
...mixins.ellipsis,
|
|
48
|
-
maxWidth: '20%',
|
|
49
|
-
'& svg': {
|
|
50
|
-
height: tokens.fontSizeBase300
|
|
51
|
-
},
|
|
52
|
-
'& button': {
|
|
53
|
-
padding: 0,
|
|
54
|
-
minWidth: 0,
|
|
55
|
-
minHeight: 0,
|
|
56
|
-
height: '14px'
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
listItemMediaNoTrim: {
|
|
60
|
-
overflow: 'visible',
|
|
61
|
-
maxWidth: 'fit-content'
|
|
62
|
-
},
|
|
63
|
-
listItemMultilineContent: {
|
|
64
|
-
whiteSpace: 'pre-line'
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
export interface iListItem {
|
|
69
|
-
key: string | number;
|
|
70
|
-
media?: JSX.Element | string;
|
|
71
|
-
header: string;
|
|
72
|
-
headerMedia?: JSX.Element | string;
|
|
73
|
-
content?: string | JSX.Element | (string | JSX.Element)[];
|
|
74
|
-
onClickOnMedia?: boolean;
|
|
75
|
-
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
|
76
|
-
selected?: boolean;
|
|
77
|
-
}
|
|
78
|
-
interface IProps {
|
|
79
|
-
selectable?: boolean;
|
|
80
|
-
items: iListItem[];
|
|
81
|
-
showAllHeaderMedia?: boolean;
|
|
82
|
-
/** allow multiline content */
|
|
83
|
-
multiline?: boolean;
|
|
84
|
-
dark?: boolean;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export const ListEx = (props: IProps) => {
|
|
88
|
-
|
|
89
|
-
const cssNames = useStyles();
|
|
90
|
-
|
|
91
|
-
const listItemElm = (item: iListItem) => <Horizontal key={item.key} css={[cssNames.listItem, item.selected && cssNames.listItemSelected]} onClick={item.onClick}>
|
|
92
|
-
{item.media && <Section css={[cssNames.media]} onClick={(e) => {
|
|
93
|
-
if (!item.onClickOnMedia)
|
|
94
|
-
e.stopPropagation();//media may have its on onclick
|
|
95
|
-
}}>{
|
|
96
|
-
isString(item.media)
|
|
97
|
-
? <div className={cssNames.image} style={{ backgroundImage: `url('${encodeURI(item.media)}'), url('${props.dark ? LOGO_WHITE_SQUARE : LOGO_BLUE_SQUARE}')` }}></div>
|
|
98
|
-
: item.media
|
|
99
|
-
}</Section>}
|
|
100
|
-
<Vertical main css={[cssNames.listItemBody]}>
|
|
101
|
-
<Horizontal main>
|
|
102
|
-
<Section main css={[cssNames.listItemHeader]}>{item.header}</Section>
|
|
103
|
-
{item.headerMedia && <Section onClick={(e) => {
|
|
104
|
-
e.stopPropagation();//media may have its on onclick
|
|
105
|
-
}} css={[cssNames.listItemMedia, props.showAllHeaderMedia && cssNames.listItemMediaNoTrim]}>{item.headerMedia}</Section>}
|
|
106
|
-
</Horizontal>
|
|
107
|
-
{!isNullOrUndefined(item.content)
|
|
108
|
-
? (Array.isArray(item.content) ? item.content : [item.content]).map((c, idx) => isNullOrUndefined(c) ? undefined : <Section key={idx} css={[cssNames.listItemContent, props.multiline ? cssNames.listItemMultilineContent : undefined]}>{c}</Section>)
|
|
109
|
-
: undefined}
|
|
110
|
-
</Vertical>
|
|
111
|
-
</Horizontal>;
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<Vertical css={[cssNames.list, KnownClassNames.list]}>
|
|
115
|
-
{props.items.map(item => listItemElm(item))}
|
|
116
|
-
</Vertical>
|
|
117
|
-
);
|
|
1
|
+
import { makeStyles, tokens } from '@fluentui/react-components';
|
|
2
|
+
import { LOGO_BLUE_SQUARE, LOGO_WHITE_SQUARE, isNullOrUndefined, isString } from '@kwiz/common';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { KnownClassNames, mixins } from '../styles/styles';
|
|
5
|
+
import { Horizontal } from './horizontal';
|
|
6
|
+
import { Section } from './section';
|
|
7
|
+
import { Vertical } from './vertical';
|
|
8
|
+
|
|
9
|
+
const useStyles = makeStyles({
|
|
10
|
+
list: {
|
|
11
|
+
rowGap: 0
|
|
12
|
+
},
|
|
13
|
+
listItem: {
|
|
14
|
+
padding: tokens.spacingVerticalS,
|
|
15
|
+
':hover': {
|
|
16
|
+
backgroundColor: tokens.colorNeutralBackground1Hover
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
listItemSelected: {
|
|
20
|
+
backgroundColor: tokens.colorNeutralBackground1Selected
|
|
21
|
+
},
|
|
22
|
+
media: {
|
|
23
|
+
width: '32px',
|
|
24
|
+
fontSize: tokens.fontSizeBase600,
|
|
25
|
+
display: 'flex',
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
justifyContent: 'center'
|
|
28
|
+
},
|
|
29
|
+
image: {
|
|
30
|
+
width: tokens.lineHeightBase600,
|
|
31
|
+
height: tokens.lineHeightBase600,
|
|
32
|
+
backgroundPosition: 'center center',
|
|
33
|
+
backgroundSize: 'cover',
|
|
34
|
+
borderRadius: tokens.borderRadiusCircular,
|
|
35
|
+
border: `1px solid ${tokens.colorNeutralStroke1}`
|
|
36
|
+
},
|
|
37
|
+
listItemBody: {
|
|
38
|
+
rowGap: 0,
|
|
39
|
+
width: 'calc(100% - 44px)'
|
|
40
|
+
},
|
|
41
|
+
listItemHeader: mixins.ellipsis,
|
|
42
|
+
listItemContent: {
|
|
43
|
+
...mixins.ellipsis,
|
|
44
|
+
fontSize: tokens.fontSizeBase200
|
|
45
|
+
},
|
|
46
|
+
listItemMedia: {
|
|
47
|
+
...mixins.ellipsis,
|
|
48
|
+
maxWidth: '20%',
|
|
49
|
+
'& svg': {
|
|
50
|
+
height: tokens.fontSizeBase300
|
|
51
|
+
},
|
|
52
|
+
'& button': {
|
|
53
|
+
padding: 0,
|
|
54
|
+
minWidth: 0,
|
|
55
|
+
minHeight: 0,
|
|
56
|
+
height: '14px'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
listItemMediaNoTrim: {
|
|
60
|
+
overflow: 'visible',
|
|
61
|
+
maxWidth: 'fit-content'
|
|
62
|
+
},
|
|
63
|
+
listItemMultilineContent: {
|
|
64
|
+
whiteSpace: 'pre-line'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export interface iListItem {
|
|
69
|
+
key: string | number;
|
|
70
|
+
media?: JSX.Element | string;
|
|
71
|
+
header: string;
|
|
72
|
+
headerMedia?: JSX.Element | string;
|
|
73
|
+
content?: string | JSX.Element | (string | JSX.Element)[];
|
|
74
|
+
onClickOnMedia?: boolean;
|
|
75
|
+
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
|
76
|
+
selected?: boolean;
|
|
77
|
+
}
|
|
78
|
+
interface IProps {
|
|
79
|
+
selectable?: boolean;
|
|
80
|
+
items: iListItem[];
|
|
81
|
+
showAllHeaderMedia?: boolean;
|
|
82
|
+
/** allow multiline content */
|
|
83
|
+
multiline?: boolean;
|
|
84
|
+
dark?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const ListEx = (props: IProps) => {
|
|
88
|
+
|
|
89
|
+
const cssNames = useStyles();
|
|
90
|
+
|
|
91
|
+
const listItemElm = (item: iListItem) => <Horizontal key={item.key} css={[cssNames.listItem, item.selected && cssNames.listItemSelected]} onClick={item.onClick}>
|
|
92
|
+
{item.media && <Section css={[cssNames.media]} onClick={(e) => {
|
|
93
|
+
if (!item.onClickOnMedia)
|
|
94
|
+
e.stopPropagation();//media may have its on onclick
|
|
95
|
+
}}>{
|
|
96
|
+
isString(item.media)
|
|
97
|
+
? <div className={cssNames.image} style={{ backgroundImage: `url('${encodeURI(item.media)}'), url('${props.dark ? LOGO_WHITE_SQUARE : LOGO_BLUE_SQUARE}')` }}></div>
|
|
98
|
+
: item.media
|
|
99
|
+
}</Section>}
|
|
100
|
+
<Vertical main css={[cssNames.listItemBody]}>
|
|
101
|
+
<Horizontal main>
|
|
102
|
+
<Section main css={[cssNames.listItemHeader]}>{item.header}</Section>
|
|
103
|
+
{item.headerMedia && <Section onClick={(e) => {
|
|
104
|
+
e.stopPropagation();//media may have its on onclick
|
|
105
|
+
}} css={[cssNames.listItemMedia, props.showAllHeaderMedia && cssNames.listItemMediaNoTrim]}>{item.headerMedia}</Section>}
|
|
106
|
+
</Horizontal>
|
|
107
|
+
{!isNullOrUndefined(item.content)
|
|
108
|
+
? (Array.isArray(item.content) ? item.content : [item.content]).map((c, idx) => isNullOrUndefined(c) ? undefined : <Section key={idx} css={[cssNames.listItemContent, props.multiline ? cssNames.listItemMultilineContent : undefined]}>{c}</Section>)
|
|
109
|
+
: undefined}
|
|
110
|
+
</Vertical>
|
|
111
|
+
</Horizontal>;
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Vertical css={[cssNames.list, KnownClassNames.list]}>
|
|
115
|
+
{props.items.map(item => listItemElm(item))}
|
|
116
|
+
</Vertical>
|
|
117
|
+
);
|
|
118
118
|
}
|
package/src/controls/loading.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { LOGO_ANIM_SMALL } from '@kwiz/common';
|
|
2
|
-
import { Centered } from './centered';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
|
|
5
|
-
interface IProps {
|
|
6
|
-
}
|
|
7
|
-
export const Loading: React.FunctionComponent<IProps> = (props) => {
|
|
8
|
-
return (
|
|
9
|
-
<Centered><img src={LOGO_ANIM_SMALL} alt="loading" style={{ width: '15vw' }} /></Centered>
|
|
10
|
-
);
|
|
1
|
+
import { LOGO_ANIM_SMALL } from '@kwiz/common';
|
|
2
|
+
import { Centered } from './centered';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
interface IProps {
|
|
6
|
+
}
|
|
7
|
+
export const Loading: React.FunctionComponent<IProps> = (props) => {
|
|
8
|
+
return (
|
|
9
|
+
<Centered><img src={LOGO_ANIM_SMALL} alt="loading" style={{ width: '15vw' }} /></Centered>
|
|
10
|
+
);
|
|
11
11
|
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { Field, ProgressBar } from '@fluentui/react-components';
|
|
2
|
-
import { isFunction } from '@kwiz/common';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { IPrompterProps, Prompter } from './prompt';
|
|
5
|
-
|
|
6
|
-
interface IProps {
|
|
7
|
-
step?: number; max?: number;
|
|
8
|
-
/** do not wrap in a dialog */
|
|
9
|
-
contentOnly?: boolean;
|
|
10
|
-
cancelText?: string;
|
|
11
|
-
onCancel?: () => void;
|
|
12
|
-
label?: string;
|
|
13
|
-
}
|
|
14
|
-
export const PleaseWait: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
|
15
|
-
const field = <Field validationMessage={props.label || "please wait..."} validationState="none">
|
|
16
|
-
<ProgressBar value={props.step} max={props.max} />
|
|
17
|
-
</Field>;
|
|
18
|
-
return (props.contentOnly
|
|
19
|
-
? field
|
|
20
|
-
: <Prompter hideOk
|
|
21
|
-
hideCancel={!isFunction(props.onCancel)}
|
|
22
|
-
cancelButtonText={props.cancelText || 'cancel'}
|
|
23
|
-
onCancel={props.onCancel}>{field}</Prompter>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const PleaseWaitPrompt = (props: { message: string; step?: number; max?: number; }): IPrompterProps => ({
|
|
28
|
-
//title: 'please wait...',
|
|
29
|
-
hideOk: true, hideCancel: true,
|
|
30
|
-
children: <Field validationMessage={props.message} validationState="none">
|
|
31
|
-
<ProgressBar value={props.step} max={props.max} />
|
|
32
|
-
</Field>
|
|
1
|
+
import { Field, ProgressBar } from '@fluentui/react-components';
|
|
2
|
+
import { isFunction } from '@kwiz/common';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { IPrompterProps, Prompter } from './prompt';
|
|
5
|
+
|
|
6
|
+
interface IProps {
|
|
7
|
+
step?: number; max?: number;
|
|
8
|
+
/** do not wrap in a dialog */
|
|
9
|
+
contentOnly?: boolean;
|
|
10
|
+
cancelText?: string;
|
|
11
|
+
onCancel?: () => void;
|
|
12
|
+
label?: string;
|
|
13
|
+
}
|
|
14
|
+
export const PleaseWait: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
|
15
|
+
const field = <Field validationMessage={props.label || "please wait..."} validationState="none">
|
|
16
|
+
<ProgressBar value={props.step} max={props.max} />
|
|
17
|
+
</Field>;
|
|
18
|
+
return (props.contentOnly
|
|
19
|
+
? field
|
|
20
|
+
: <Prompter hideOk
|
|
21
|
+
hideCancel={!isFunction(props.onCancel)}
|
|
22
|
+
cancelButtonText={props.cancelText || 'cancel'}
|
|
23
|
+
onCancel={props.onCancel}>{field}</Prompter>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const PleaseWaitPrompt = (props: { message: string; step?: number; max?: number; }): IPrompterProps => ({
|
|
28
|
+
//title: 'please wait...',
|
|
29
|
+
hideOk: true, hideCancel: true,
|
|
30
|
+
children: <Field validationMessage={props.message} validationState="none">
|
|
31
|
+
<ProgressBar value={props.step} max={props.max} />
|
|
32
|
+
</Field>
|
|
33
33
|
});
|
package/src/controls/prompt.tsx
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
import { Dialog, DialogActions, DialogBody, DialogContent, DialogModalType, DialogSurface, DialogTitle, DialogTrigger } from '@fluentui/react-components';
|
|
2
|
-
import { DismissRegular } from '@fluentui/react-icons';
|
|
3
|
-
import { isNotEmptyArray, isNullOrEmptyString } from '@kwiz/common';
|
|
4
|
-
import React from 'react';
|
|
5
|
-
import { useKWIZFluentContext } from '../helpers/context';
|
|
6
|
-
import { useKeyDown } from '../helpers/hooks';
|
|
7
|
-
import { ButtonEX, ButtonEXProps, ButtonEXSecondary } from './button';
|
|
8
|
-
|
|
9
|
-
export interface IPrompterProps {
|
|
10
|
-
hideOk?: boolean;
|
|
11
|
-
hideCancel?: boolean;
|
|
12
|
-
showCancelInTitle?: boolean;
|
|
13
|
-
/** return false to prevent closing the dialog. */
|
|
14
|
-
onOK?: () => Promise<void> | void;
|
|
15
|
-
onCancel?: () => void;
|
|
16
|
-
okButtonText?: string;
|
|
17
|
-
cancelButtonText?: string;
|
|
18
|
-
title?: string | JSX.Element;
|
|
19
|
-
okButtonProps?: Partial<ButtonEXProps>;
|
|
20
|
-
cancelButtonProps?: Partial<ButtonEXProps>;
|
|
21
|
-
children?: JSX.Element;
|
|
22
|
-
|
|
23
|
-
/** allow to epand the dialog to be wider */
|
|
24
|
-
maxWidth?: number | string;
|
|
25
|
-
|
|
26
|
-
/** additional button actions at the bottom */
|
|
27
|
-
actions?: JSX.Element[];
|
|
28
|
-
|
|
29
|
-
/** dialog title actions */
|
|
30
|
-
titleActions?: JSX.Element[];
|
|
31
|
-
/** specify a specific mount node for this dialog */
|
|
32
|
-
mountNode?: HTMLElement | null | {
|
|
33
|
-
element?: HTMLElement | null;
|
|
34
|
-
className?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
modalType?: DialogModalType;
|
|
38
|
-
/** do not fire ok/cancel on esc/enter press */
|
|
39
|
-
disableKeyboardActions?: boolean;
|
|
40
|
-
}
|
|
41
|
-
export const Prompter = React.forwardRef<HTMLDivElement, (IPrompterProps)>((props, ref) => {
|
|
42
|
-
const ctx = useKWIZFluentContext();
|
|
43
|
-
const disableKeyboardActions = React.useRef(props.disableKeyboardActions);
|
|
44
|
-
disableKeyboardActions.current = props.disableKeyboardActions;
|
|
45
|
-
|
|
46
|
-
let okProps: ButtonEXProps = {
|
|
47
|
-
...(props.okButtonProps as any || {}),
|
|
48
|
-
onClick: () => props.onOK(),
|
|
49
|
-
title: props.okButtonText || 'yes'
|
|
50
|
-
};
|
|
51
|
-
let cancelProps: ButtonEXProps = {
|
|
52
|
-
...(props.cancelButtonProps as any || {}),
|
|
53
|
-
onClick: () => props.onCancel(),
|
|
54
|
-
title: props.cancelButtonText || 'no'
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
useKeyDown({
|
|
58
|
-
onEnter: () => !disableKeyboardActions.current && props.onOK(),
|
|
59
|
-
onEscape: () => !disableKeyboardActions.current && props.onCancel(),
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const actions: JSX.Element[] = [];
|
|
63
|
-
if (!props.hideOk) actions.push(<DialogTrigger key='ok' disableButtonEnhancement>
|
|
64
|
-
<ButtonEXSecondary {...okProps} />
|
|
65
|
-
</DialogTrigger>);
|
|
66
|
-
if (!props.hideCancel) actions.push(<DialogTrigger key='cancel' disableButtonEnhancement>
|
|
67
|
-
<ButtonEXSecondary {...cancelProps} />
|
|
68
|
-
</DialogTrigger>);
|
|
69
|
-
if (isNotEmptyArray(props.actions))
|
|
70
|
-
actions.push(...props.actions);
|
|
71
|
-
|
|
72
|
-
const titleActions: JSX.Element[] = props.titleActions ? [...props.titleActions] : [];
|
|
73
|
-
if (props.showCancelInTitle)
|
|
74
|
-
titleActions.push(<DialogTrigger key='cancel' disableButtonEnhancement>
|
|
75
|
-
<ButtonEX {...cancelProps} icon={<DismissRegular />} />
|
|
76
|
-
</DialogTrigger>);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<Dialog open modalType={props.modalType}>
|
|
81
|
-
<DialogSurface mountNode={props.mountNode || ctx.mountNode}
|
|
82
|
-
style={!isNullOrEmptyString(props.maxWidth) ? { maxWidth: props.maxWidth } : undefined}>
|
|
83
|
-
<DialogBody>
|
|
84
|
-
{(!isNullOrEmptyString(props.title) || isNotEmptyArray(titleActions)) && <DialogTitle
|
|
85
|
-
action={titleActions}
|
|
86
|
-
>{props.title}</DialogTitle>}
|
|
87
|
-
<DialogContent ref={ref}>
|
|
88
|
-
{props.children}
|
|
89
|
-
</DialogContent>
|
|
90
|
-
{isNotEmptyArray(actions) && <DialogActions fluid={actions.length > 2}>
|
|
91
|
-
{actions}
|
|
92
|
-
</DialogActions>}
|
|
93
|
-
</DialogBody>
|
|
94
|
-
</DialogSurface>
|
|
95
|
-
</Dialog>
|
|
96
|
-
);
|
|
1
|
+
import { Dialog, DialogActions, DialogBody, DialogContent, DialogModalType, DialogSurface, DialogTitle, DialogTrigger } from '@fluentui/react-components';
|
|
2
|
+
import { DismissRegular } from '@fluentui/react-icons';
|
|
3
|
+
import { isNotEmptyArray, isNullOrEmptyString } from '@kwiz/common';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useKWIZFluentContext } from '../helpers/context';
|
|
6
|
+
import { useKeyDown } from '../helpers/hooks';
|
|
7
|
+
import { ButtonEX, ButtonEXProps, ButtonEXSecondary } from './button';
|
|
8
|
+
|
|
9
|
+
export interface IPrompterProps {
|
|
10
|
+
hideOk?: boolean;
|
|
11
|
+
hideCancel?: boolean;
|
|
12
|
+
showCancelInTitle?: boolean;
|
|
13
|
+
/** return false to prevent closing the dialog. */
|
|
14
|
+
onOK?: () => Promise<void> | void;
|
|
15
|
+
onCancel?: () => void;
|
|
16
|
+
okButtonText?: string;
|
|
17
|
+
cancelButtonText?: string;
|
|
18
|
+
title?: string | JSX.Element;
|
|
19
|
+
okButtonProps?: Partial<ButtonEXProps>;
|
|
20
|
+
cancelButtonProps?: Partial<ButtonEXProps>;
|
|
21
|
+
children?: JSX.Element;
|
|
22
|
+
|
|
23
|
+
/** allow to epand the dialog to be wider */
|
|
24
|
+
maxWidth?: number | string;
|
|
25
|
+
|
|
26
|
+
/** additional button actions at the bottom */
|
|
27
|
+
actions?: JSX.Element[];
|
|
28
|
+
|
|
29
|
+
/** dialog title actions */
|
|
30
|
+
titleActions?: JSX.Element[];
|
|
31
|
+
/** specify a specific mount node for this dialog */
|
|
32
|
+
mountNode?: HTMLElement | null | {
|
|
33
|
+
element?: HTMLElement | null;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
modalType?: DialogModalType;
|
|
38
|
+
/** do not fire ok/cancel on esc/enter press */
|
|
39
|
+
disableKeyboardActions?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export const Prompter = React.forwardRef<HTMLDivElement, (IPrompterProps)>((props, ref) => {
|
|
42
|
+
const ctx = useKWIZFluentContext();
|
|
43
|
+
const disableKeyboardActions = React.useRef(props.disableKeyboardActions);
|
|
44
|
+
disableKeyboardActions.current = props.disableKeyboardActions;
|
|
45
|
+
|
|
46
|
+
let okProps: ButtonEXProps = {
|
|
47
|
+
...(props.okButtonProps as any || {}),
|
|
48
|
+
onClick: () => props.onOK(),
|
|
49
|
+
title: props.okButtonText || 'yes'
|
|
50
|
+
};
|
|
51
|
+
let cancelProps: ButtonEXProps = {
|
|
52
|
+
...(props.cancelButtonProps as any || {}),
|
|
53
|
+
onClick: () => props.onCancel(),
|
|
54
|
+
title: props.cancelButtonText || 'no'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
useKeyDown({
|
|
58
|
+
onEnter: () => !disableKeyboardActions.current && props.onOK(),
|
|
59
|
+
onEscape: () => !disableKeyboardActions.current && props.onCancel(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const actions: JSX.Element[] = [];
|
|
63
|
+
if (!props.hideOk) actions.push(<DialogTrigger key='ok' disableButtonEnhancement>
|
|
64
|
+
<ButtonEXSecondary {...okProps} />
|
|
65
|
+
</DialogTrigger>);
|
|
66
|
+
if (!props.hideCancel) actions.push(<DialogTrigger key='cancel' disableButtonEnhancement>
|
|
67
|
+
<ButtonEXSecondary {...cancelProps} />
|
|
68
|
+
</DialogTrigger>);
|
|
69
|
+
if (isNotEmptyArray(props.actions))
|
|
70
|
+
actions.push(...props.actions);
|
|
71
|
+
|
|
72
|
+
const titleActions: JSX.Element[] = props.titleActions ? [...props.titleActions] : [];
|
|
73
|
+
if (props.showCancelInTitle)
|
|
74
|
+
titleActions.push(<DialogTrigger key='cancel' disableButtonEnhancement>
|
|
75
|
+
<ButtonEX {...cancelProps} icon={<DismissRegular />} />
|
|
76
|
+
</DialogTrigger>);
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Dialog open modalType={props.modalType}>
|
|
81
|
+
<DialogSurface mountNode={props.mountNode || ctx.mountNode}
|
|
82
|
+
style={!isNullOrEmptyString(props.maxWidth) ? { maxWidth: props.maxWidth } : undefined}>
|
|
83
|
+
<DialogBody>
|
|
84
|
+
{(!isNullOrEmptyString(props.title) || isNotEmptyArray(titleActions)) && <DialogTitle
|
|
85
|
+
action={titleActions}
|
|
86
|
+
>{props.title}</DialogTitle>}
|
|
87
|
+
<DialogContent ref={ref}>
|
|
88
|
+
{props.children}
|
|
89
|
+
</DialogContent>
|
|
90
|
+
{isNotEmptyArray(actions) && <DialogActions fluid={actions.length > 2}>
|
|
91
|
+
{actions}
|
|
92
|
+
</DialogActions>}
|
|
93
|
+
</DialogBody>
|
|
94
|
+
</DialogSurface>
|
|
95
|
+
</Dialog>
|
|
96
|
+
);
|
|
97
97
|
});
|
package/src/controls/search.tsx
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
import { Input, InputProps, makeStyles } from '@fluentui/react-components';
|
|
2
|
-
import { DismissRegular, SearchRegular } from "@fluentui/react-icons";
|
|
3
|
-
import { debounce, isFunction, isNullOrEmptyString } from '@kwiz/common';
|
|
4
|
-
import React, { useState } from 'react';
|
|
5
|
-
import { GetLogger } from '../_modules/config';
|
|
6
|
-
import { useStateEX } from '../helpers/hooks';
|
|
7
|
-
import { mixins } from '../styles/styles';
|
|
8
|
-
const logger = GetLogger("Search");
|
|
9
|
-
|
|
10
|
-
const useStyles = makeStyles({
|
|
11
|
-
main: mixins.main,
|
|
12
|
-
clickable: mixins.clickable,
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
interface IProps extends InputProps {
|
|
16
|
-
main?: boolean;
|
|
17
|
-
delay?: number;
|
|
18
|
-
/** if changing the value in the caller - change this prop to reset */
|
|
19
|
-
resetValue?: string;
|
|
20
|
-
onChangeDeferred?: (newValue: string) => void;
|
|
21
|
-
onChangeSync?: (newValue: string) => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** value is set on first load. to change the value after it was first set - change the compoenet's key. */
|
|
25
|
-
export const Search: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
|
26
|
-
const cssNames = useStyles();
|
|
27
|
-
|
|
28
|
-
const [resetKey, setResetKey] = useState(1);
|
|
29
|
-
|
|
30
|
-
let delay = props.delay || 1;
|
|
31
|
-
|
|
32
|
-
//cannot call debounce every render, since it won't be the same debounced instance...
|
|
33
|
-
var notifyParent = React.useMemo(() => debounce(v => {
|
|
34
|
-
logger.log(`Set: ${v}`);
|
|
35
|
-
props.onChangeDeferred(v);
|
|
36
|
-
}, delay * 1000), [delay]);
|
|
37
|
-
|
|
38
|
-
let [value, setValue] = useStateEX(props.value, {
|
|
39
|
-
onChange: newValue => {
|
|
40
|
-
if (isFunction(props.onChangeSync)) props.onChangeSync(newValue as string);
|
|
41
|
-
if (isFunction(props.onChangeDeferred)) notifyParent(newValue);
|
|
42
|
-
return newValue;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
//once props change, reset this control value to match
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
setValue(props.resetValue);
|
|
49
|
-
//todo: bug: setting value does not sync into the text box
|
|
50
|
-
setResetKey(resetKey + 1)
|
|
51
|
-
}, [props.resetValue]);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<Input key={resetKey} {...props} value={value} onChange={(e, data) => setValue(data.value)}
|
|
55
|
-
className={props.main ? cssNames.main : undefined}
|
|
56
|
-
contentBefore={!isNullOrEmptyString(value) ? undefined : <SearchRegular />}
|
|
57
|
-
contentAfter={isNullOrEmptyString(value)
|
|
58
|
-
? undefined
|
|
59
|
-
: <DismissRegular className={cssNames.clickable} onClick={() => {
|
|
60
|
-
setValue("");
|
|
61
|
-
//todo: bug: setting value does not sync into the text box
|
|
62
|
-
setResetKey(resetKey + 1)
|
|
63
|
-
}} />
|
|
64
|
-
} />
|
|
65
|
-
);
|
|
1
|
+
import { Input, InputProps, makeStyles } from '@fluentui/react-components';
|
|
2
|
+
import { DismissRegular, SearchRegular } from "@fluentui/react-icons";
|
|
3
|
+
import { debounce, isFunction, isNullOrEmptyString } from '@kwiz/common';
|
|
4
|
+
import React, { useState } from 'react';
|
|
5
|
+
import { GetLogger } from '../_modules/config';
|
|
6
|
+
import { useStateEX } from '../helpers/hooks';
|
|
7
|
+
import { mixins } from '../styles/styles';
|
|
8
|
+
const logger = GetLogger("Search");
|
|
9
|
+
|
|
10
|
+
const useStyles = makeStyles({
|
|
11
|
+
main: mixins.main,
|
|
12
|
+
clickable: mixins.clickable,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
interface IProps extends InputProps {
|
|
16
|
+
main?: boolean;
|
|
17
|
+
delay?: number;
|
|
18
|
+
/** if changing the value in the caller - change this prop to reset */
|
|
19
|
+
resetValue?: string;
|
|
20
|
+
onChangeDeferred?: (newValue: string) => void;
|
|
21
|
+
onChangeSync?: (newValue: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** value is set on first load. to change the value after it was first set - change the compoenet's key. */
|
|
25
|
+
export const Search: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
|
26
|
+
const cssNames = useStyles();
|
|
27
|
+
|
|
28
|
+
const [resetKey, setResetKey] = useState(1);
|
|
29
|
+
|
|
30
|
+
let delay = props.delay || 1;
|
|
31
|
+
|
|
32
|
+
//cannot call debounce every render, since it won't be the same debounced instance...
|
|
33
|
+
var notifyParent = React.useMemo(() => debounce(v => {
|
|
34
|
+
logger.log(`Set: ${v}`);
|
|
35
|
+
props.onChangeDeferred(v);
|
|
36
|
+
}, delay * 1000), [delay]);
|
|
37
|
+
|
|
38
|
+
let [value, setValue] = useStateEX(props.value, {
|
|
39
|
+
onChange: newValue => {
|
|
40
|
+
if (isFunction(props.onChangeSync)) props.onChangeSync(newValue as string);
|
|
41
|
+
if (isFunction(props.onChangeDeferred)) notifyParent(newValue);
|
|
42
|
+
return newValue;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
//once props change, reset this control value to match
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
setValue(props.resetValue);
|
|
49
|
+
//todo: bug: setting value does not sync into the text box
|
|
50
|
+
setResetKey(resetKey + 1)
|
|
51
|
+
}, [props.resetValue]);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Input key={resetKey} {...props} value={value} onChange={(e, data) => setValue(data.value)}
|
|
55
|
+
className={props.main ? cssNames.main : undefined}
|
|
56
|
+
contentBefore={!isNullOrEmptyString(value) ? undefined : <SearchRegular />}
|
|
57
|
+
contentAfter={isNullOrEmptyString(value)
|
|
58
|
+
? undefined
|
|
59
|
+
: <DismissRegular className={cssNames.clickable} onClick={() => {
|
|
60
|
+
setValue("");
|
|
61
|
+
//todo: bug: setting value does not sync into the text box
|
|
62
|
+
setResetKey(resetKey + 1)
|
|
63
|
+
}} />
|
|
64
|
+
} />
|
|
65
|
+
);
|
|
66
66
|
}
|