@kwiz/fluentui 1.0.15 → 1.0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. package/.github/workflows/npm-publish.yml +34 -0
  2. package/LICENSE +21 -21
  3. package/README.md +26 -26
  4. package/package.json +72 -72
  5. package/src/_modules/config.ts +9 -9
  6. package/src/_modules/constants.ts +3 -3
  7. package/src/controls/accordion.tsx +48 -0
  8. package/src/controls/button.tsx +169 -169
  9. package/src/controls/centered.tsx +22 -22
  10. package/src/controls/date.tsx +39 -39
  11. package/src/controls/dropdown.tsx +51 -51
  12. package/src/controls/error-boundary.tsx +41 -41
  13. package/src/controls/field-editor.tsx +40 -40
  14. package/src/controls/file-upload.tsx +67 -67
  15. package/src/controls/horizontal.tsx +34 -34
  16. package/src/controls/input.tsx +60 -60
  17. package/src/controls/kwizoverflow.tsx +103 -102
  18. package/src/controls/list.tsx +117 -117
  19. package/src/controls/loading.tsx +10 -10
  20. package/src/controls/please-wait.tsx +32 -32
  21. package/src/controls/prompt.tsx +96 -96
  22. package/src/controls/search.tsx +65 -65
  23. package/src/controls/section.tsx +51 -51
  24. package/src/controls/svg.tsx +120 -120
  25. package/src/controls/toolbar.tsx +48 -48
  26. package/src/controls/vertical-content.tsx +49 -49
  27. package/src/controls/vertical.tsx +34 -34
  28. package/src/helpers/context.ts +39 -39
  29. package/src/helpers/hooks.tsx +335 -335
  30. package/src/index.ts +26 -25
  31. package/src/styles/styles.ts +87 -87
  32. package/src/styles/theme.ts +90 -90
  33. package/dist/_modules/build.d.ts +0 -2
  34. package/dist/_modules/build.js +0 -3
  35. package/dist/_modules/build.js.map +0 -1
  36. package/dist/_modules/config.d.ts +0 -1
  37. package/dist/_modules/config.js +0 -9
  38. package/dist/_modules/config.js.map +0 -1
  39. package/dist/_modules/constants.d.ts +0 -2
  40. package/dist/_modules/constants.js +0 -3
  41. package/dist/_modules/constants.js.map +0 -1
  42. package/dist/_modules/exports-index.d.ts +0 -1
  43. package/dist/_modules/exports-index.js +0 -2
  44. package/dist/_modules/exports-index.js.map +0 -1
  45. package/dist/controls/button.d.ts +0 -28
  46. package/dist/controls/button.js +0 -113
  47. package/dist/controls/button.js.map +0 -1
  48. package/dist/controls/centered.d.ts +0 -5
  49. package/dist/controls/centered.js +0 -14
  50. package/dist/controls/centered.js.map +0 -1
  51. package/dist/controls/date.d.ts +0 -8
  52. package/dist/controls/date.js +0 -32
  53. package/dist/controls/date.js.map +0 -1
  54. package/dist/controls/dropdown.d.ts +0 -29
  55. package/dist/controls/dropdown.js +0 -27
  56. package/dist/controls/dropdown.js.map +0 -1
  57. package/dist/controls/error-boundary.d.ts +0 -23
  58. package/dist/controls/error-boundary.js +0 -33
  59. package/dist/controls/error-boundary.js.map +0 -1
  60. package/dist/controls/exports-index.d.ts +0 -17
  61. package/dist/controls/exports-index.js +0 -18
  62. package/dist/controls/exports-index.js.map +0 -1
  63. package/dist/controls/field-editor.d.ts +0 -13
  64. package/dist/controls/field-editor.js +0 -15
  65. package/dist/controls/field-editor.js.map +0 -1
  66. package/dist/controls/file-upload.d.ts +0 -18
  67. package/dist/controls/file-upload.js +0 -41
  68. package/dist/controls/file-upload.js.map +0 -1
  69. package/dist/controls/horizontal.d.ts +0 -8
  70. package/dist/controls/horizontal.js +0 -23
  71. package/dist/controls/horizontal.js.map +0 -1
  72. package/dist/controls/input.d.ts +0 -13
  73. package/dist/controls/input.js +0 -43
  74. package/dist/controls/input.js.map +0 -1
  75. package/dist/controls/kwizoverflow.d.ts +0 -13
  76. package/dist/controls/kwizoverflow.js +0 -45
  77. package/dist/controls/kwizoverflow.js.map +0 -1
  78. package/dist/controls/list.d.ts +0 -21
  79. package/dist/controls/list.js +0 -72
  80. package/dist/controls/list.js.map +0 -1
  81. package/dist/controls/loading copy.d.ts +0 -5
  82. package/dist/controls/loading copy.js +0 -7
  83. package/dist/controls/loading copy.js.map +0 -1
  84. package/dist/controls/loading.d.ts +0 -5
  85. package/dist/controls/loading.js +0 -7
  86. package/dist/controls/loading.js.map +0 -1
  87. package/dist/controls/please-wait.d.ts +0 -18
  88. package/dist/controls/please-wait.js +0 -16
  89. package/dist/controls/please-wait.js.map +0 -1
  90. package/dist/controls/prompt.d.ts +0 -32
  91. package/dist/controls/prompt.js +0 -31
  92. package/dist/controls/prompt.js.map +0 -1
  93. package/dist/controls/search.d.ts +0 -13
  94. package/dist/controls/search.js +0 -47
  95. package/dist/controls/search.js.map +0 -1
  96. package/dist/controls/section.d.ts +0 -14
  97. package/dist/controls/section.js +0 -27
  98. package/dist/controls/section.js.map +0 -1
  99. package/dist/controls/svg.d.ts +0 -23
  100. package/dist/controls/svg.js +0 -45
  101. package/dist/controls/svg.js.map +0 -1
  102. package/dist/controls/toolbar.d.ts +0 -12
  103. package/dist/controls/toolbar.js +0 -23
  104. package/dist/controls/toolbar.js.map +0 -1
  105. package/dist/controls/vertical-content.d.ts +0 -6
  106. package/dist/controls/vertical-content.js +0 -37
  107. package/dist/controls/vertical-content.js.map +0 -1
  108. package/dist/controls/vertical.d.ts +0 -8
  109. package/dist/controls/vertical.js +0 -23
  110. package/dist/controls/vertical.js.map +0 -1
  111. package/dist/exports-index.d.ts +0 -3
  112. package/dist/exports-index.js +0 -4
  113. package/dist/exports-index.js.map +0 -1
  114. package/dist/helpers/context.d.ts +0 -26
  115. package/dist/helpers/context.js +0 -15
  116. package/dist/helpers/context.js.map +0 -1
  117. package/dist/helpers/hooks.d.ts +0 -62
  118. package/dist/helpers/hooks.js +0 -287
  119. package/dist/helpers/hooks.js.map +0 -1
  120. package/dist/index.d.ts +0 -24
  121. package/dist/index.js +0 -24
  122. package/dist/index.js.map +0 -1
  123. package/dist/styles/exports-index.d.ts +0 -2
  124. package/dist/styles/exports-index.js +0 -3
  125. package/dist/styles/exports-index.js.map +0 -1
  126. package/dist/styles/styles.d.ts +0 -19
  127. package/dist/styles/styles.js +0 -79
  128. package/dist/styles/styles.js.map +0 -1
  129. package/dist/styles/theme.d.ts +0 -6
  130. package/dist/styles/theme.js +0 -77
  131. package/dist/styles/theme.js.map +0 -1
@@ -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
  }
@@ -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
  });
@@ -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
  });
@@ -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
  }