@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.
Files changed (92) hide show
  1. package/.github/workflows/npm-publish.yml +24 -24
  2. package/LICENSE +21 -21
  3. package/README.md +53 -53
  4. package/dist/@types/forwardRef.d.ts +0 -0
  5. package/dist/@types/forwardRef.js +1 -0
  6. package/dist/@types/forwardRef.js.map +1 -0
  7. package/dist/controls/error-boundary copy.d.ts +23 -0
  8. package/dist/controls/error-boundary copy.js +33 -0
  9. package/dist/controls/error-boundary copy.js.map +1 -0
  10. package/dist/controls/menu.js +2 -2
  11. package/dist/controls/menu.js.map +1 -1
  12. package/dist/controls/search.js +19 -11
  13. package/dist/controls/search.js.map +1 -1
  14. package/dist/controls/svg.js +21 -21
  15. package/dist/controls/svg.js.map +1 -1
  16. package/dist/helpers/common.d.ts +4 -0
  17. package/dist/helpers/common.js +2 -0
  18. package/dist/helpers/common.js.map +1 -0
  19. package/dist/helpers/context.d.ts +26 -0
  20. package/dist/helpers/context.js +15 -0
  21. package/dist/helpers/context.js.map +1 -0
  22. package/dist/helpers/drag-drop/exports.d.ts +12 -0
  23. package/dist/helpers/drag-drop/exports.js +3 -0
  24. package/dist/helpers/drag-drop/exports.js.map +1 -0
  25. package/dist/helpers/exports.d.ts +7 -0
  26. package/dist/helpers/exports.js +8 -0
  27. package/dist/helpers/exports.js.map +1 -0
  28. package/dist/helpers/use-editable-control.d.ts +1 -1
  29. package/dist/helpers/use-editable-control.js.map +1 -1
  30. package/package.json +85 -84
  31. package/src/_modules/config.ts +9 -9
  32. package/src/_modules/constants.ts +3 -3
  33. package/src/controls/ColorPickerDialog.tsx +83 -83
  34. package/src/controls/accordion.tsx +62 -62
  35. package/src/controls/button.tsx +180 -180
  36. package/src/controls/canvas/CustomEventTargetBase.ts +32 -32
  37. package/src/controls/canvas/DrawPad.tsx +296 -296
  38. package/src/controls/canvas/DrawPadManager.ts +694 -694
  39. package/src/controls/canvas/bezier.ts +109 -109
  40. package/src/controls/canvas/point.ts +44 -44
  41. package/src/controls/card-list.tsx +31 -31
  42. package/src/controls/card.tsx +77 -77
  43. package/src/controls/centered.tsx +14 -14
  44. package/src/controls/date.tsx +87 -87
  45. package/src/controls/diagram-picker.tsx +96 -96
  46. package/src/controls/divider.tsx +15 -15
  47. package/src/controls/dropdown.tsx +66 -66
  48. package/src/controls/error-boundary.tsx +41 -41
  49. package/src/controls/field-editor.tsx +42 -42
  50. package/src/controls/file-upload.tsx +155 -155
  51. package/src/controls/horizontal.tsx +48 -48
  52. package/src/controls/html-editor/editor.tsx +182 -182
  53. package/src/controls/index.ts +33 -33
  54. package/src/controls/input.tsx +160 -160
  55. package/src/controls/kwizoverflow.tsx +106 -106
  56. package/src/controls/list.tsx +119 -119
  57. package/src/controls/loading.tsx +10 -10
  58. package/src/controls/menu.tsx +173 -173
  59. package/src/controls/merge-text.tsx +126 -126
  60. package/src/controls/please-wait.tsx +32 -32
  61. package/src/controls/progress-bar.tsx +109 -109
  62. package/src/controls/prompt.tsx +121 -121
  63. package/src/controls/qrcode.tsx +36 -36
  64. package/src/controls/search.tsx +71 -61
  65. package/src/controls/section.tsx +133 -133
  66. package/src/controls/svg.tsx +138 -138
  67. package/src/controls/toolbar.tsx +46 -46
  68. package/src/controls/vertical-content.tsx +49 -49
  69. package/src/controls/vertical.tsx +42 -42
  70. package/src/helpers/block-nav.tsx +88 -88
  71. package/src/helpers/context-const.ts +29 -29
  72. package/src/helpers/context-export.tsx +77 -77
  73. package/src/helpers/context-internal.ts +13 -13
  74. package/src/helpers/drag-drop/drag-drop-container.tsx +53 -53
  75. package/src/helpers/drag-drop/drag-drop-context-internal.tsx +9 -9
  76. package/src/helpers/drag-drop/drag-drop-context.tsx +61 -61
  77. package/src/helpers/drag-drop/drag-drop.types.ts +21 -21
  78. package/src/helpers/drag-drop/index.ts +12 -12
  79. package/src/helpers/drag-drop/readme.md +75 -75
  80. package/src/helpers/drag-drop/use-draggable.ts +47 -47
  81. package/src/helpers/drag-drop/use-droppable.ts +38 -38
  82. package/src/helpers/forwardRef.ts +7 -7
  83. package/src/helpers/hooks-events.ts +149 -149
  84. package/src/helpers/hooks.tsx +141 -141
  85. package/src/helpers/index.ts +8 -8
  86. package/src/helpers/use-alerts.tsx +74 -74
  87. package/src/helpers/use-editable-control.tsx +37 -37
  88. package/src/helpers/use-toast.tsx +29 -29
  89. package/src/index.ts +2 -2
  90. package/src/styles/index.ts +1 -1
  91. package/src/styles/styles.ts +104 -104
  92. package/src/styles/theme.ts +90 -90
@@ -1,88 +1,88 @@
1
- import { DatePicker, DatePickerProps } from '@fluentui/react-datepicker-compat';
2
- import { TimePicker, TimePickerProps } from '@fluentui/react-timepicker-compat';
3
-
4
- import { CalendarCancelRegular } from '@fluentui/react-icons';
5
- import { isDate } from '@kwiz/common';
6
- import * as React from 'react';
7
- import { useKWIZFluentContext } from '../helpers/context-internal';
8
- import { useStateEX } from '../helpers';
9
- import { Horizontal } from './horizontal';
10
-
11
- interface IProps {
12
- onDateChange: (newDateObject: Date) => void;
13
- value: Date;
14
- showTime?: boolean;
15
- datePickerProps?: DatePickerProps;
16
- timePickerProps?: TimePickerProps;
17
- /** don't allow to clear the value */
18
- required?: boolean;
19
- }
20
- export const DatePickerEx: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
21
- const ctx = useKWIZFluentContext();
22
-
23
- //time value will always have a value even when clearing the date
24
- const [timeValue, setTimeValue] = useStateEX<Date>(isDate(props.value) ? props.value : new Date());
25
- const { showClear, dateValue } = React.useMemo(() => {
26
- const showClear = !props.required && isDate(props.value);
27
- const dateValue = props.value;
28
- return { showClear, dateValue };
29
- }, [props.value]);
30
-
31
- function reset() {
32
- props.onDateChange(null);
33
- }
34
-
35
- const changeDateHandler = React.useCallback((newDateValue: Date): void => {
36
- const newDate = new Date(newDateValue);
37
- // Use the old time values.
38
- newDate.setHours(
39
- timeValue ? timeValue.getHours() : 0,
40
- timeValue ? timeValue.getMinutes() : 0, 0, 0
41
- );
42
- props.onDateChange(newDate);
43
- }, [timeValue, props.onDateChange]);
44
-
45
- const changeTimeHandler = React.useCallback((newTimeValue: Date): void => {
46
- //update our state
47
- setTimeValue(newTimeValue);
48
- // Use the old date value.
49
- const newDate = isDate(dateValue) ? new Date(dateValue) : new Date();
50
- newDate.setHours(
51
- newTimeValue.getHours(),
52
- newTimeValue.getMinutes(), 0, 0
53
- );
54
- props.onDateChange(newDate);
55
- }, [dateValue]);
56
-
57
- const DatePickerControl = <DatePicker
58
- {...(props.datePickerProps || {})}
59
- appearance={ctx.inputAppearance}
60
- mountNode={ctx.mountNode}
61
- value={isDate(dateValue) ? dateValue : null}
62
- onSelectDate={(newDate) => {
63
- changeDateHandler(newDate);
64
- }}
65
- contentBefore={showClear && <CalendarCancelRegular title='Clear' onClick={() => reset()} />}
66
- />
67
-
68
- const TimePickerControl = <TimePicker
69
- appearance={ctx.inputAppearance}
70
- mountNode={ctx.mountNode}
71
- {...props.timePickerProps}
72
- //only show time value when there is a selected date. timeValue will never be null.
73
- value={isDate(dateValue) ? timeValue.toLocaleTimeString("en", { hour: "2-digit", minute: "2-digit", hour12: true }) : ""}
74
- onTimeChange={(e, date) => {
75
- const newDate = date.selectedTime;
76
- changeTimeHandler(newDate);
77
- }}
78
- />
79
-
80
- return (
81
- props.showTime
82
- ? <Horizontal>
83
- {DatePickerControl}
84
- {TimePickerControl}
85
- </Horizontal>
86
- : DatePickerControl
87
- );
1
+ import { DatePicker, DatePickerProps } from '@fluentui/react-datepicker-compat';
2
+ import { TimePicker, TimePickerProps } from '@fluentui/react-timepicker-compat';
3
+
4
+ import { CalendarCancelRegular } from '@fluentui/react-icons';
5
+ import { isDate } from '@kwiz/common';
6
+ import * as React from 'react';
7
+ import { useKWIZFluentContext } from '../helpers/context-internal';
8
+ import { useStateEX } from '../helpers';
9
+ import { Horizontal } from './horizontal';
10
+
11
+ interface IProps {
12
+ onDateChange: (newDateObject: Date) => void;
13
+ value: Date;
14
+ showTime?: boolean;
15
+ datePickerProps?: DatePickerProps;
16
+ timePickerProps?: TimePickerProps;
17
+ /** don't allow to clear the value */
18
+ required?: boolean;
19
+ }
20
+ export const DatePickerEx: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
21
+ const ctx = useKWIZFluentContext();
22
+
23
+ //time value will always have a value even when clearing the date
24
+ const [timeValue, setTimeValue] = useStateEX<Date>(isDate(props.value) ? props.value : new Date());
25
+ const { showClear, dateValue } = React.useMemo(() => {
26
+ const showClear = !props.required && isDate(props.value);
27
+ const dateValue = props.value;
28
+ return { showClear, dateValue };
29
+ }, [props.value]);
30
+
31
+ function reset() {
32
+ props.onDateChange(null);
33
+ }
34
+
35
+ const changeDateHandler = React.useCallback((newDateValue: Date): void => {
36
+ const newDate = new Date(newDateValue);
37
+ // Use the old time values.
38
+ newDate.setHours(
39
+ timeValue ? timeValue.getHours() : 0,
40
+ timeValue ? timeValue.getMinutes() : 0, 0, 0
41
+ );
42
+ props.onDateChange(newDate);
43
+ }, [timeValue, props.onDateChange]);
44
+
45
+ const changeTimeHandler = React.useCallback((newTimeValue: Date): void => {
46
+ //update our state
47
+ setTimeValue(newTimeValue);
48
+ // Use the old date value.
49
+ const newDate = isDate(dateValue) ? new Date(dateValue) : new Date();
50
+ newDate.setHours(
51
+ newTimeValue.getHours(),
52
+ newTimeValue.getMinutes(), 0, 0
53
+ );
54
+ props.onDateChange(newDate);
55
+ }, [dateValue]);
56
+
57
+ const DatePickerControl = <DatePicker
58
+ {...(props.datePickerProps || {})}
59
+ appearance={ctx.inputAppearance}
60
+ mountNode={ctx.mountNode}
61
+ value={isDate(dateValue) ? dateValue : null}
62
+ onSelectDate={(newDate) => {
63
+ changeDateHandler(newDate);
64
+ }}
65
+ contentBefore={showClear && <CalendarCancelRegular title='Clear' onClick={() => reset()} />}
66
+ />
67
+
68
+ const TimePickerControl = <TimePicker
69
+ appearance={ctx.inputAppearance}
70
+ mountNode={ctx.mountNode}
71
+ {...props.timePickerProps}
72
+ //only show time value when there is a selected date. timeValue will never be null.
73
+ value={isDate(dateValue) ? timeValue.toLocaleTimeString("en", { hour: "2-digit", minute: "2-digit", hour12: true }) : ""}
74
+ onTimeChange={(e, date) => {
75
+ const newDate = date.selectedTime;
76
+ changeTimeHandler(newDate);
77
+ }}
78
+ />
79
+
80
+ return (
81
+ props.showTime
82
+ ? <Horizontal>
83
+ {DatePickerControl}
84
+ {TimePickerControl}
85
+ </Horizontal>
86
+ : DatePickerControl
87
+ );
88
88
  }
@@ -1,97 +1,97 @@
1
- import { Dialog, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger, makeStyles, shorthands, Switch, tokens } from '@fluentui/react-components';
2
- import { ImageSparkleRegular } from '@fluentui/react-icons';
3
- import { DiagramOptions, stockUrl } from '@kwiz/common';
4
- import React from 'react';
5
- import { useKWIZFluentContext } from '../helpers/context-internal';
6
- import { useStateEX } from '../helpers';
7
- import { ButtonEXPrimarySubtle } from './button';
8
- import { Horizontal } from './horizontal';
9
- import { Section } from './section';
10
-
11
- const useStyles = makeStyles({
12
- dialog: {
13
- maxWidth: '70vw',
14
- width: '70vw',
15
- },
16
- dialogBody: {
17
- maxHeight: '60vh',
18
- },
19
- diagramWrapper: {
20
- justifyContent: "center",
21
- alignContent: "center"
22
- },
23
- diagram: {
24
- border: `1px solid ${tokens.colorNeutralStroke1}`,
25
- padding: tokens.spacingHorizontalS,
26
- "&:hover": {
27
- ...shorthands.borderColor(tokens.colorBrandBackground),
28
- },
29
- "&>img": {
30
- width: "180px"
31
- },
32
- },
33
- });
34
-
35
- interface iProps {
36
- onSelect?: (diagram: { url: string; name: string; }) => void;
37
- onSelectBase64?: (diagram: string) => void;
38
- trigger?: JSX.Element;
39
- hiRes?: boolean;
40
- onlyTransparent?: boolean;
41
- }
42
- export const DiagramPicker = React.forwardRef<HTMLDivElement, (React.PropsWithChildren<iProps>)>((props, ref) => {
43
- const ctx = useKWIZFluentContext();
44
- const classes = useStyles();
45
- const [isOpen, setIsOpen] = useStateEX(false);
46
- const [hiRes, setHiRes] = useStateEX(props.hiRes);
47
- let options = (hiRes ? DiagramOptions.hiRes : DiagramOptions.options);
48
- if (props.onlyTransparent) options = options.filter(o => o.name.endsWith(', transparent'));
49
- return (
50
- <Dialog open={isOpen} onOpenChange={(e, data) => {
51
- setIsOpen(data.open);
52
- }}>
53
- <DialogTrigger disableButtonEnhancement>
54
- {props.trigger || <ButtonEXPrimarySubtle icon={<ImageSparkleRegular />} title='Open gallery' showTitleWithIcon dontCenterText />}
55
- </DialogTrigger>
56
- <DialogSurface mountNode={ctx.mountNode} className={classes.dialog}>
57
- <DialogBody className={classes.dialogBody}>
58
- <DialogTitle>Choose a diagram</DialogTitle>
59
- <DialogContent>
60
- <Switch checked={hiRes === true}
61
- onChange={(e, data) => {
62
- setHiRes(data.checked === true);
63
- }}
64
- label="High resolution diagrams"
65
- />
66
- <Horizontal main wrap css={[classes.diagramWrapper]}>
67
- {options
68
- .map(diagram => <Section key={diagram.name} css={[classes.diagram]}
69
- title={diagram.name}
70
- onClick={async () => {
71
- const fullUrl = `${stockUrl}/${diagram.url}`;
72
- props.onSelect?.({ name: diagram.name, url: fullUrl });
73
- if (props.onSelectBase64) {
74
- const result = await fetch(fullUrl);
75
- const blob = await result.blob();
76
- const reader = new FileReader();
77
- reader.onload = function () {
78
- props.onSelectBase64?.(reader.result as string);
79
- };
80
- reader.readAsDataURL(blob);
81
- }
82
- setIsOpen(false);
83
- }}>
84
- <img src={`${stockUrl}/${diagram.url}`} />
85
- </Section>)}
86
- </Horizontal>
87
- </DialogContent>
88
- {/* <DialogActions>
89
- <DialogTrigger disableButtonEnhancement>
90
- <Button appearance="secondary">Cancel</Button>
91
- </DialogTrigger>
92
- <Button appearance="primary">Save</Button>
93
- </DialogActions> */}
94
- </DialogBody>
95
- </DialogSurface>
96
- </Dialog>);
1
+ import { Dialog, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger, makeStyles, shorthands, Switch, tokens } from '@fluentui/react-components';
2
+ import { ImageSparkleRegular } from '@fluentui/react-icons';
3
+ import { DiagramOptions, stockUrl } from '@kwiz/common';
4
+ import React from 'react';
5
+ import { useKWIZFluentContext } from '../helpers/context-internal';
6
+ import { useStateEX } from '../helpers';
7
+ import { ButtonEXPrimarySubtle } from './button';
8
+ import { Horizontal } from './horizontal';
9
+ import { Section } from './section';
10
+
11
+ const useStyles = makeStyles({
12
+ dialog: {
13
+ maxWidth: '70vw',
14
+ width: '70vw',
15
+ },
16
+ dialogBody: {
17
+ maxHeight: '60vh',
18
+ },
19
+ diagramWrapper: {
20
+ justifyContent: "center",
21
+ alignContent: "center"
22
+ },
23
+ diagram: {
24
+ border: `1px solid ${tokens.colorNeutralStroke1}`,
25
+ padding: tokens.spacingHorizontalS,
26
+ "&:hover": {
27
+ ...shorthands.borderColor(tokens.colorBrandBackground),
28
+ },
29
+ "&>img": {
30
+ width: "180px"
31
+ },
32
+ },
33
+ });
34
+
35
+ interface iProps {
36
+ onSelect?: (diagram: { url: string; name: string; }) => void;
37
+ onSelectBase64?: (diagram: string) => void;
38
+ trigger?: JSX.Element;
39
+ hiRes?: boolean;
40
+ onlyTransparent?: boolean;
41
+ }
42
+ export const DiagramPicker = React.forwardRef<HTMLDivElement, (React.PropsWithChildren<iProps>)>((props, ref) => {
43
+ const ctx = useKWIZFluentContext();
44
+ const classes = useStyles();
45
+ const [isOpen, setIsOpen] = useStateEX(false);
46
+ const [hiRes, setHiRes] = useStateEX(props.hiRes);
47
+ let options = (hiRes ? DiagramOptions.hiRes : DiagramOptions.options);
48
+ if (props.onlyTransparent) options = options.filter(o => o.name.endsWith(', transparent'));
49
+ return (
50
+ <Dialog open={isOpen} onOpenChange={(e, data) => {
51
+ setIsOpen(data.open);
52
+ }}>
53
+ <DialogTrigger disableButtonEnhancement>
54
+ {props.trigger || <ButtonEXPrimarySubtle icon={<ImageSparkleRegular />} title='Open gallery' showTitleWithIcon dontCenterText />}
55
+ </DialogTrigger>
56
+ <DialogSurface mountNode={ctx.mountNode} className={classes.dialog}>
57
+ <DialogBody className={classes.dialogBody}>
58
+ <DialogTitle>Choose a diagram</DialogTitle>
59
+ <DialogContent>
60
+ <Switch checked={hiRes === true}
61
+ onChange={(e, data) => {
62
+ setHiRes(data.checked === true);
63
+ }}
64
+ label="High resolution diagrams"
65
+ />
66
+ <Horizontal main wrap css={[classes.diagramWrapper]}>
67
+ {options
68
+ .map(diagram => <Section key={diagram.name} css={[classes.diagram]}
69
+ title={diagram.name}
70
+ onClick={async () => {
71
+ const fullUrl = `${stockUrl}/${diagram.url}`;
72
+ props.onSelect?.({ name: diagram.name, url: fullUrl });
73
+ if (props.onSelectBase64) {
74
+ const result = await fetch(fullUrl);
75
+ const blob = await result.blob();
76
+ const reader = new FileReader();
77
+ reader.onload = function () {
78
+ props.onSelectBase64?.(reader.result as string);
79
+ };
80
+ reader.readAsDataURL(blob);
81
+ }
82
+ setIsOpen(false);
83
+ }}>
84
+ <img src={`${stockUrl}/${diagram.url}`} />
85
+ </Section>)}
86
+ </Horizontal>
87
+ </DialogContent>
88
+ {/* <DialogActions>
89
+ <DialogTrigger disableButtonEnhancement>
90
+ <Button appearance="secondary">Cancel</Button>
91
+ </DialogTrigger>
92
+ <Button appearance="primary">Save</Button>
93
+ </DialogActions> */}
94
+ </DialogBody>
95
+ </DialogSurface>
96
+ </Dialog>);
97
97
  });
@@ -1,16 +1,16 @@
1
- import { Divider, DividerProps, makeStyles, mergeClasses } from '@fluentui/react-components';
2
- import React from 'react';
3
-
4
- const useStyles = makeStyles({
5
- separator: {
6
- flexGrow: 0
7
- }
8
- });
9
- interface IProps extends DividerProps {
10
- }
11
- export const DividerEX = React.forwardRef<HTMLDivElement, (React.PropsWithChildren<IProps>)>((props, ref) => {
12
- const cssNames = useStyles();
13
- return (
14
- <Divider ref={ref} {...props} className={mergeClasses(cssNames.separator, props.className)} />
15
- );
1
+ import { Divider, DividerProps, makeStyles, mergeClasses } from '@fluentui/react-components';
2
+ import React from 'react';
3
+
4
+ const useStyles = makeStyles({
5
+ separator: {
6
+ flexGrow: 0
7
+ }
8
+ });
9
+ interface IProps extends DividerProps {
10
+ }
11
+ export const DividerEX = React.forwardRef<HTMLDivElement, (React.PropsWithChildren<IProps>)>((props, ref) => {
12
+ const cssNames = useStyles();
13
+ return (
14
+ <Divider ref={ref} {...props} className={mergeClasses(cssNames.separator, props.className)} />
15
+ );
16
16
  });
@@ -1,67 +1,67 @@
1
- import { Dropdown, DropdownProps, makeStyles, mergeClasses, Option } from '@fluentui/react-components';
2
- import { filterEmptyEntries, firstOrNull, isNullOrUndefined } from '@kwiz/common';
3
- import React from 'react';
4
- import { GetLogger } from '../_modules/config';
5
- import { useKWIZFluentContext } from '../helpers/context-internal';
6
-
7
- const logger = GetLogger("DropdownEX");
8
-
9
- const useStyles = makeStyles({
10
- root: {
11
- minWidth: "auto"
12
- },
13
- });
14
-
15
- type ForwardProps = Omit<DropdownProps, "onSelect" | "selectedOptions" | "clearable">;
16
-
17
- interface IProps<keyType, dataType> extends ForwardProps {
18
- required?: boolean;
19
- selected: keyType | keyType[];
20
- items: {
21
- key: keyType, value: string, data?: dataType,
22
- /** display complex controls in the drop down */
23
- option?: JSX.Element;
24
- }[];
25
- onSelect: (
26
- /** the specific option that was selected/unselected */
27
- option: { key: keyType, value: string, data?: dataType },
28
- /** only sent for multi select - all selected options, in case of multi select */
29
- options?: { key: keyType, value: string, data?: dataType }[]) => void;
30
- }
31
-
32
- function $DropdownEX<keyType extends string = string, dataType = never>(props: IProps<keyType, dataType>, ref: React.ForwardedRef<HTMLButtonElement>) {
33
- const classes = useStyles();
34
- const ctx = useKWIZFluentContext();
35
- const selected: keyType[] = Array.isArray(props.selected) ? props.selected : isNullOrUndefined(props.selected) ? [] : [props.selected];
36
-
37
- //sometimes control will lose value when re-rendered
38
- //use case: public forms when editing other fields after the dropdown was set
39
- //re-set the text value manually to fix
40
- let text = filterEmptyEntries((Array.isArray(props.selected) ? props.selected : [props.selected]).map(s => {
41
- let v = firstOrNull(props.items, i => i.key === s);
42
- return v ? v.value : ''
43
- })).join(', ');
44
-
45
- return (
46
- <Dropdown {...{ ...props, onSelect: undefined }} className={mergeClasses(classes.root, props.className)} ref={ref} clearable={!props.required && !props.multiselect}
47
- appearance={ctx.inputAppearance} mountNode={ctx.mountNode}
48
- selectedOptions={selected} value={text} onOptionSelect={(e, data) => {
49
- let o = firstOrNull(props.items, i => i.key === data.optionValue);
50
- if (props.multiselect) {
51
- let current = data.selectedOptions.map(s => firstOrNull(props.items, i => i.key === s));
52
- props.onSelect(o, current);
53
- }
54
- else props.onSelect(o);
55
- }}>
56
- {props.items.map(i => <Option key={i.key} value={i.key} text={i.value}>{i.option ? i.option : i.value}</Option>)}
57
- </Dropdown>
58
- );
59
- }
60
-
61
- export const DropdownEX = React.forwardRef($DropdownEX);
62
-
63
- /** @deprecated use normal DropdownEX it is now generic */
64
- export function getDropdownEX<keyType extends string = string, dataType = never>() {
65
- logger.warn('getDropdownEX is deprecated. use DropdownEX it now supports generic types');
66
- return React.forwardRef($DropdownEX<keyType, dataType>);
1
+ import { Dropdown, DropdownProps, makeStyles, mergeClasses, Option } from '@fluentui/react-components';
2
+ import { filterEmptyEntries, firstOrNull, isNullOrUndefined } from '@kwiz/common';
3
+ import React from 'react';
4
+ import { GetLogger } from '../_modules/config';
5
+ import { useKWIZFluentContext } from '../helpers/context-internal';
6
+
7
+ const logger = GetLogger("DropdownEX");
8
+
9
+ const useStyles = makeStyles({
10
+ root: {
11
+ minWidth: "auto"
12
+ },
13
+ });
14
+
15
+ type ForwardProps = Omit<DropdownProps, "onSelect" | "selectedOptions" | "clearable">;
16
+
17
+ interface IProps<keyType, dataType> extends ForwardProps {
18
+ required?: boolean;
19
+ selected: keyType | keyType[];
20
+ items: {
21
+ key: keyType, value: string, data?: dataType,
22
+ /** display complex controls in the drop down */
23
+ option?: JSX.Element;
24
+ }[];
25
+ onSelect: (
26
+ /** the specific option that was selected/unselected */
27
+ option: { key: keyType, value: string, data?: dataType },
28
+ /** only sent for multi select - all selected options, in case of multi select */
29
+ options?: { key: keyType, value: string, data?: dataType }[]) => void;
30
+ }
31
+
32
+ function $DropdownEX<keyType extends string = string, dataType = never>(props: IProps<keyType, dataType>, ref: React.ForwardedRef<HTMLButtonElement>) {
33
+ const classes = useStyles();
34
+ const ctx = useKWIZFluentContext();
35
+ const selected: keyType[] = Array.isArray(props.selected) ? props.selected : isNullOrUndefined(props.selected) ? [] : [props.selected];
36
+
37
+ //sometimes control will lose value when re-rendered
38
+ //use case: public forms when editing other fields after the dropdown was set
39
+ //re-set the text value manually to fix
40
+ let text = filterEmptyEntries((Array.isArray(props.selected) ? props.selected : [props.selected]).map(s => {
41
+ let v = firstOrNull(props.items, i => i.key === s);
42
+ return v ? v.value : ''
43
+ })).join(', ');
44
+
45
+ return (
46
+ <Dropdown {...{ ...props, onSelect: undefined }} className={mergeClasses(classes.root, props.className)} ref={ref} clearable={!props.required && !props.multiselect}
47
+ appearance={ctx.inputAppearance} mountNode={ctx.mountNode}
48
+ selectedOptions={selected} value={text} onOptionSelect={(e, data) => {
49
+ let o = firstOrNull(props.items, i => i.key === data.optionValue);
50
+ if (props.multiselect) {
51
+ let current = data.selectedOptions.map(s => firstOrNull(props.items, i => i.key === s));
52
+ props.onSelect(o, current);
53
+ }
54
+ else props.onSelect(o);
55
+ }}>
56
+ {props.items.map(i => <Option key={i.key} value={i.key} text={i.value}>{i.option ? i.option : i.value}</Option>)}
57
+ </Dropdown>
58
+ );
59
+ }
60
+
61
+ export const DropdownEX = React.forwardRef($DropdownEX);
62
+
63
+ /** @deprecated use normal DropdownEX it is now generic */
64
+ export function getDropdownEX<keyType extends string = string, dataType = never>() {
65
+ logger.warn('getDropdownEX is deprecated. use DropdownEX it now supports generic types');
66
+ return React.forwardRef($DropdownEX<keyType, dataType>);
67
67
  }
@@ -1,42 +1,42 @@
1
- import * as React from "react";
2
- import { GetLogger } from "../_modules/config";
3
-
4
- const logger = GetLogger("ErrorBoundary");
5
-
6
- interface iProps {
7
- errorComponent?: JSX.Element,
8
- /** If changeMarker changes, it will check the error again */
9
- changeMarker: string | number
10
- }
11
- interface iState { hasError: boolean; marker: string | number; }
12
- export class ErrorBoundary extends React.Component<React.PropsWithChildren<iProps>, iState> {
13
- constructor(props: iProps) {
14
- super(props);
15
- this.state = { hasError: false, marker: props.changeMarker };
16
- }
17
-
18
- static getDerivedStateFromError(error) {
19
- // Update state so the next render will show the fallback UI.
20
- return { hasError: true };
21
- }
22
- static getDerivedStateFromProps(props: iProps, state: iState) {
23
- if (props.changeMarker !== state.marker)
24
- return { hasError: false, marker: props.changeMarker };
25
- else return null;
26
- }
27
-
28
- componentDidCatch(error, errorInfo) {
29
- // You can also log the error to an error reporting service
30
- logger.error(error);
31
- logger.error(errorInfo);
32
- }
33
-
34
- render() {
35
- if (this.state.hasError) {
36
- // You can render any custom fallback UI
37
- return this.props.errorComponent || <h1>Something went wrong.</h1>;
38
- }
39
-
40
- return this.props.children;
41
- }
1
+ import * as React from "react";
2
+ import { GetLogger } from "../_modules/config";
3
+
4
+ const logger = GetLogger("ErrorBoundary");
5
+
6
+ interface iProps {
7
+ errorComponent?: JSX.Element,
8
+ /** If changeMarker changes, it will check the error again */
9
+ changeMarker: string | number
10
+ }
11
+ interface iState { hasError: boolean; marker: string | number; }
12
+ export class ErrorBoundary extends React.Component<React.PropsWithChildren<iProps>, iState> {
13
+ constructor(props: iProps) {
14
+ super(props);
15
+ this.state = { hasError: false, marker: props.changeMarker };
16
+ }
17
+
18
+ static getDerivedStateFromError(error) {
19
+ // Update state so the next render will show the fallback UI.
20
+ return { hasError: true };
21
+ }
22
+ static getDerivedStateFromProps(props: iProps, state: iState) {
23
+ if (props.changeMarker !== state.marker)
24
+ return { hasError: false, marker: props.changeMarker };
25
+ else return null;
26
+ }
27
+
28
+ componentDidCatch(error, errorInfo) {
29
+ // You can also log the error to an error reporting service
30
+ logger.error(error);
31
+ logger.error(errorInfo);
32
+ }
33
+
34
+ render() {
35
+ if (this.state.hasError) {
36
+ // You can render any custom fallback UI
37
+ return this.props.errorComponent || <h1>Something went wrong.</h1>;
38
+ }
39
+
40
+ return this.props.children;
41
+ }
42
42
  }