@kwiz/fluentui 1.0.74 → 1.0.76

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 (94) 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.d.ts +3 -1
  11. package/dist/controls/menu.js +44 -26
  12. package/dist/controls/menu.js.map +1 -1
  13. package/dist/controls/search.js +19 -11
  14. package/dist/controls/search.js.map +1 -1
  15. package/dist/controls/svg.js +21 -21
  16. package/dist/controls/svg.js.map +1 -1
  17. package/dist/helpers/common.d.ts +4 -0
  18. package/dist/helpers/common.js +2 -0
  19. package/dist/helpers/common.js.map +1 -0
  20. package/dist/helpers/context.d.ts +26 -0
  21. package/dist/helpers/context.js +15 -0
  22. package/dist/helpers/context.js.map +1 -0
  23. package/dist/helpers/drag-drop/exports.d.ts +12 -0
  24. package/dist/helpers/drag-drop/exports.js +3 -0
  25. package/dist/helpers/drag-drop/exports.js.map +1 -0
  26. package/dist/helpers/exports.d.ts +7 -0
  27. package/dist/helpers/exports.js +8 -0
  28. package/dist/helpers/exports.js.map +1 -0
  29. package/dist/helpers/hooks.d.ts +3 -1
  30. package/dist/helpers/hooks.js +18 -0
  31. package/dist/helpers/hooks.js.map +1 -1
  32. package/package.json +85 -84
  33. package/src/_modules/config.ts +9 -9
  34. package/src/_modules/constants.ts +3 -3
  35. package/src/controls/ColorPickerDialog.tsx +83 -83
  36. package/src/controls/accordion.tsx +62 -62
  37. package/src/controls/button.tsx +180 -180
  38. package/src/controls/canvas/CustomEventTargetBase.ts +32 -32
  39. package/src/controls/canvas/DrawPad.tsx +296 -296
  40. package/src/controls/canvas/DrawPadManager.ts +694 -694
  41. package/src/controls/canvas/bezier.ts +109 -109
  42. package/src/controls/canvas/point.ts +44 -44
  43. package/src/controls/card-list.tsx +31 -31
  44. package/src/controls/card.tsx +77 -77
  45. package/src/controls/centered.tsx +14 -14
  46. package/src/controls/date.tsx +87 -87
  47. package/src/controls/diagram-picker.tsx +96 -96
  48. package/src/controls/divider.tsx +15 -15
  49. package/src/controls/dropdown.tsx +66 -66
  50. package/src/controls/error-boundary.tsx +41 -41
  51. package/src/controls/field-editor.tsx +42 -42
  52. package/src/controls/file-upload.tsx +155 -155
  53. package/src/controls/horizontal.tsx +48 -48
  54. package/src/controls/html-editor/editor.tsx +182 -182
  55. package/src/controls/index.ts +33 -33
  56. package/src/controls/input.tsx +160 -160
  57. package/src/controls/kwizoverflow.tsx +106 -106
  58. package/src/controls/list.tsx +119 -119
  59. package/src/controls/loading.tsx +10 -10
  60. package/src/controls/menu.tsx +195 -173
  61. package/src/controls/merge-text.tsx +126 -126
  62. package/src/controls/please-wait.tsx +32 -32
  63. package/src/controls/progress-bar.tsx +109 -109
  64. package/src/controls/prompt.tsx +121 -121
  65. package/src/controls/qrcode.tsx +36 -36
  66. package/src/controls/search.tsx +71 -61
  67. package/src/controls/section.tsx +133 -133
  68. package/src/controls/svg.tsx +138 -138
  69. package/src/controls/toolbar.tsx +46 -46
  70. package/src/controls/vertical-content.tsx +49 -49
  71. package/src/controls/vertical.tsx +42 -42
  72. package/src/helpers/block-nav.tsx +88 -88
  73. package/src/helpers/context-const.ts +29 -29
  74. package/src/helpers/context-export.tsx +77 -77
  75. package/src/helpers/context-internal.ts +13 -13
  76. package/src/helpers/drag-drop/drag-drop-container.tsx +53 -53
  77. package/src/helpers/drag-drop/drag-drop-context-internal.tsx +9 -9
  78. package/src/helpers/drag-drop/drag-drop-context.tsx +61 -61
  79. package/src/helpers/drag-drop/drag-drop.types.ts +21 -21
  80. package/src/helpers/drag-drop/index.ts +12 -12
  81. package/src/helpers/drag-drop/readme.md +75 -75
  82. package/src/helpers/drag-drop/use-draggable.ts +47 -47
  83. package/src/helpers/drag-drop/use-droppable.ts +38 -38
  84. package/src/helpers/forwardRef.ts +7 -7
  85. package/src/helpers/hooks-events.ts +149 -149
  86. package/src/helpers/hooks.tsx +162 -141
  87. package/src/helpers/index.ts +8 -8
  88. package/src/helpers/use-alerts.tsx +74 -74
  89. package/src/helpers/use-editable-control.tsx +37 -37
  90. package/src/helpers/use-toast.tsx +29 -29
  91. package/src/index.ts +2 -2
  92. package/src/styles/index.ts +1 -1
  93. package/src/styles/styles.ts +104 -104
  94. package/src/styles/theme.ts +90 -90
@@ -1,126 +1,126 @@
1
- import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, Field, Label, makeStyles, Radio, tokens } from '@fluentui/react-components';
2
- import { DismissRegular, SaveRegular } from '@fluentui/react-icons';
3
- import { isNullOrUndefined, waitFor } from '@kwiz/common';
4
- import { DefaultDarkColors, DefaultLightColors, MisMerge2 } from '@mismerge/react';
5
- import * as React from 'react';
6
-
7
- import '@mismerge/core/dark.css';
8
- import '@mismerge/core/styles.css';
9
- import { useStateEX, useWindowSize } from '../helpers';
10
- import { useKWIZFluentContext } from '../helpers/context-internal';
11
- import { ButtonEX, ButtonEXPrimarySubtle } from './button';
12
- import { Horizontal } from './horizontal';
13
- import { Section } from './section';
14
- import { Vertical } from './vertical';
15
-
16
- const useStyles = makeStyles({
17
- root: {
18
- // position: 'fixed',
19
- // top: 0,
20
- // left: 0,
21
- // right: 0,
22
- // bottom: 0,
23
- // backgroundColor: tokens.colorNeutralBackground1,
24
- // zIndex: 10,
25
- "& .mismerge": {
26
- // height: "100%",
27
- "--background": tokens.colorNeutralBackground1,
28
- //line number background
29
- "--primary-100": tokens.colorNeutralBackground2,
30
- //selection background
31
- "--selection": tokens.colorNeutralBackground1Selected,
32
- //scrollbar hover
33
- "--primary-300": tokens.colorNeutralBackground1Hover,
34
- //border / scroll
35
- "--primary-200": tokens.colorNeutralStroke1,
36
- //line number color
37
- "--primary-400": tokens.colorNeutralForeground2,
38
- //button hover color
39
- "--primary-500": tokens.colorNeutralForeground1Hover,
40
- //main text color
41
- "--primary-600": tokens.colorNeutralForeground1,
42
- "& TEXTAREA": {
43
- lineHeight: "20px"
44
- }
45
- }
46
- },
47
- menu: {
48
- justifyContent: "space-between"
49
- }
50
- });
51
-
52
- interface IProps {
53
- title: string;
54
- description?: string;
55
- lhsTitle: string;
56
- lhsValue: string;
57
- rhsTitle: string;
58
- rhsValue: string;
59
- dark?: boolean;
60
- save: (merged: string) => void;
61
- cancel: () => void;
62
- }
63
- export const MergeText: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
64
- const classes = useStyles();
65
- const ctx = useKWIZFluentContext();
66
-
67
- let size = useWindowSize();
68
- let wrapper = React.useRef<HTMLDivElement>();
69
- let [lhs, setLhs] = useStateEX(props.lhsValue || "", {
70
- skipUpdateIfSame: true, onChange: (v, changed) => {
71
- if (changed) setKeep("left"); return v;
72
- }
73
- });
74
- let [rhs, setRhs] = useStateEX(props.rhsValue || "", {
75
- skipUpdateIfSame: true, onChange: (v, changed) => {
76
- if (changed) setKeep("right"); return v;
77
- }
78
- });
79
- let [keep, setKeep] = useStateEX<"cancel" | "left" | "right">("cancel");
80
-
81
- React.useEffect(() => {
82
- if (wrapper.current) {
83
- waitFor(() => !isNullOrUndefined(wrapper.current.querySelector(".mismerge"))).then(() => {
84
- let mismerge = wrapper.current.querySelector(".mismerge") as HTMLDivElement;
85
- if (mismerge)
86
- mismerge.style.height = `${mismerge.offsetParent.clientHeight - mismerge.offsetTop - 10}px`;
87
- });
88
- }
89
- }, [wrapper.current, size.height]);
90
-
91
- return <Drawer type='overlay' open size='full' className={classes.root} mountNode={ctx.mountNode}>
92
- <DrawerHeader>
93
- <DrawerHeaderTitle action={<ButtonEX icon={<DismissRegular />} title="Close" onClick={props.cancel} />}>
94
- {props.title}
95
- </DrawerHeaderTitle>
96
- </DrawerHeader>
97
- <DrawerBody>
98
- <Vertical>
99
- {props.description && <Label>{props.description}</Label>}
100
- <Field label="Which version would you like to keep?"
101
- hint="Merge the changes to either side and save. Close this panel to keep editing the page without saving">
102
- <Horizontal css={[classes.menu]}>
103
- <Horizontal nogap>
104
- <Radio value="left" label={props.lhsTitle} checked={keep === "left"} onClick={() => setKeep("left")} />
105
- <ButtonEXPrimarySubtle showTitleWithIcon dontCenterText icon={<SaveRegular />} disabled={keep !== "left"} title={`Save ${props.lhsTitle.toLowerCase()}`} onClick={() => props.save(lhs)} />
106
- </Horizontal>
107
- <Horizontal nogap>
108
- <Radio value="right" label={props.rhsTitle} checked={keep === "right"} onClick={() => setKeep("right")} />
109
- <ButtonEXPrimarySubtle showTitleWithIcon dontCenterText icon={<SaveRegular />} disabled={keep !== "right"} title={`Save ${props.rhsTitle.toLowerCase()}`} onClick={() => props.save(rhs)} />
110
- </Horizontal>
111
- </Horizontal>
112
- </Field>
113
- <Section main ref={wrapper}>
114
- <MisMerge2
115
- lhs={lhs}
116
- rhs={rhs}
117
- lhsEditable rhsEditable
118
- onLhsChange={v => setLhs(v)}
119
- onRhsChange={v => setRhs(v)}
120
- colors={props.dark ? DefaultDarkColors : DefaultLightColors}
121
- />
122
- </Section>
123
- </Vertical>
124
- </DrawerBody>
125
- </Drawer>;
126
- }
1
+ import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, Field, Label, makeStyles, Radio, tokens } from '@fluentui/react-components';
2
+ import { DismissRegular, SaveRegular } from '@fluentui/react-icons';
3
+ import { isNullOrUndefined, waitFor } from '@kwiz/common';
4
+ import { DefaultDarkColors, DefaultLightColors, MisMerge2 } from '@mismerge/react';
5
+ import * as React from 'react';
6
+
7
+ import '@mismerge/core/dark.css';
8
+ import '@mismerge/core/styles.css';
9
+ import { useStateEX, useWindowSize } from '../helpers';
10
+ import { useKWIZFluentContext } from '../helpers/context-internal';
11
+ import { ButtonEX, ButtonEXPrimarySubtle } from './button';
12
+ import { Horizontal } from './horizontal';
13
+ import { Section } from './section';
14
+ import { Vertical } from './vertical';
15
+
16
+ const useStyles = makeStyles({
17
+ root: {
18
+ // position: 'fixed',
19
+ // top: 0,
20
+ // left: 0,
21
+ // right: 0,
22
+ // bottom: 0,
23
+ // backgroundColor: tokens.colorNeutralBackground1,
24
+ // zIndex: 10,
25
+ "& .mismerge": {
26
+ // height: "100%",
27
+ "--background": tokens.colorNeutralBackground1,
28
+ //line number background
29
+ "--primary-100": tokens.colorNeutralBackground2,
30
+ //selection background
31
+ "--selection": tokens.colorNeutralBackground1Selected,
32
+ //scrollbar hover
33
+ "--primary-300": tokens.colorNeutralBackground1Hover,
34
+ //border / scroll
35
+ "--primary-200": tokens.colorNeutralStroke1,
36
+ //line number color
37
+ "--primary-400": tokens.colorNeutralForeground2,
38
+ //button hover color
39
+ "--primary-500": tokens.colorNeutralForeground1Hover,
40
+ //main text color
41
+ "--primary-600": tokens.colorNeutralForeground1,
42
+ "& TEXTAREA": {
43
+ lineHeight: "20px"
44
+ }
45
+ }
46
+ },
47
+ menu: {
48
+ justifyContent: "space-between"
49
+ }
50
+ });
51
+
52
+ interface IProps {
53
+ title: string;
54
+ description?: string;
55
+ lhsTitle: string;
56
+ lhsValue: string;
57
+ rhsTitle: string;
58
+ rhsValue: string;
59
+ dark?: boolean;
60
+ save: (merged: string) => void;
61
+ cancel: () => void;
62
+ }
63
+ export const MergeText: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
64
+ const classes = useStyles();
65
+ const ctx = useKWIZFluentContext();
66
+
67
+ let size = useWindowSize();
68
+ let wrapper = React.useRef<HTMLDivElement>();
69
+ let [lhs, setLhs] = useStateEX(props.lhsValue || "", {
70
+ skipUpdateIfSame: true, onChange: (v, changed) => {
71
+ if (changed) setKeep("left"); return v;
72
+ }
73
+ });
74
+ let [rhs, setRhs] = useStateEX(props.rhsValue || "", {
75
+ skipUpdateIfSame: true, onChange: (v, changed) => {
76
+ if (changed) setKeep("right"); return v;
77
+ }
78
+ });
79
+ let [keep, setKeep] = useStateEX<"cancel" | "left" | "right">("cancel");
80
+
81
+ React.useEffect(() => {
82
+ if (wrapper.current) {
83
+ waitFor(() => !isNullOrUndefined(wrapper.current.querySelector(".mismerge"))).then(() => {
84
+ let mismerge = wrapper.current.querySelector(".mismerge") as HTMLDivElement;
85
+ if (mismerge)
86
+ mismerge.style.height = `${mismerge.offsetParent.clientHeight - mismerge.offsetTop - 10}px`;
87
+ });
88
+ }
89
+ }, [wrapper.current, size.height]);
90
+
91
+ return <Drawer type='overlay' open size='full' className={classes.root} mountNode={ctx.mountNode}>
92
+ <DrawerHeader>
93
+ <DrawerHeaderTitle action={<ButtonEX icon={<DismissRegular />} title="Close" onClick={props.cancel} />}>
94
+ {props.title}
95
+ </DrawerHeaderTitle>
96
+ </DrawerHeader>
97
+ <DrawerBody>
98
+ <Vertical>
99
+ {props.description && <Label>{props.description}</Label>}
100
+ <Field label="Which version would you like to keep?"
101
+ hint="Merge the changes to either side and save. Close this panel to keep editing the page without saving">
102
+ <Horizontal css={[classes.menu]}>
103
+ <Horizontal nogap>
104
+ <Radio value="left" label={props.lhsTitle} checked={keep === "left"} onClick={() => setKeep("left")} />
105
+ <ButtonEXPrimarySubtle showTitleWithIcon dontCenterText icon={<SaveRegular />} disabled={keep !== "left"} title={`Save ${props.lhsTitle.toLowerCase()}`} onClick={() => props.save(lhs)} />
106
+ </Horizontal>
107
+ <Horizontal nogap>
108
+ <Radio value="right" label={props.rhsTitle} checked={keep === "right"} onClick={() => setKeep("right")} />
109
+ <ButtonEXPrimarySubtle showTitleWithIcon dontCenterText icon={<SaveRegular />} disabled={keep !== "right"} title={`Save ${props.rhsTitle.toLowerCase()}`} onClick={() => props.save(rhs)} />
110
+ </Horizontal>
111
+ </Horizontal>
112
+ </Field>
113
+ <Section main ref={wrapper}>
114
+ <MisMerge2
115
+ lhs={lhs}
116
+ rhs={rhs}
117
+ lhsEditable rhsEditable
118
+ onLhsChange={v => setLhs(v)}
119
+ onRhsChange={v => setRhs(v)}
120
+ colors={props.dark ? DefaultDarkColors : DefaultLightColors}
121
+ />
122
+ </Section>
123
+ </Vertical>
124
+ </DrawerBody>
125
+ </Drawer>;
126
+ }
@@ -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,110 +1,110 @@
1
- import { DividerProps, makeStyles, mergeClasses, ProgressBar, tokens } from '@fluentui/react-components';
2
- import { CheckmarkRegular, FluentIcon } from '@fluentui/react-icons';
3
- import { isFunction, isNotEmptyString } from '@kwiz/common';
4
- import React from 'react';
5
- import { KnownClassNames } from '../styles/styles';
6
- import { Horizontal } from './horizontal';
7
- import { Section } from './section';
8
- import { Vertical } from './vertical';
9
-
10
- const useStyles = makeStyles({
11
- root: {
12
- position: "relative"
13
- },
14
- stepNumber: {
15
- border: `2px solid ${tokens.colorNeutralStroke1}`,
16
- borderRadius: tokens.borderRadiusCircular,
17
- width: '24px',
18
- height: '24px',
19
- position: 'relative',
20
- display: 'inline-flex',
21
- alignItems: 'center',
22
- justifyContent: 'center',
23
- backgroundColor: tokens.colorNeutralBackground1,
24
- },
25
- stepLabel: {
26
- backgroundColor: tokens.colorNeutralBackground1,
27
- position: "absolute",
28
- top: '-10px',
29
- left: 0,
30
- right: 0,
31
- "& > span": {
32
- whiteSpace: "nowrap",
33
- overflow: "hidden",
34
- textOverflow: "ellipsis",
35
- display: "inline-block"
36
- }
37
- },
38
- stepNumberCurrent: {
39
- border: `2px solid ${tokens.colorBrandBackground}`,
40
- },
41
- stepNumberCompleted: {
42
- border: `2px solid ${tokens.colorBrandBackground}`,
43
- backgroundColor: tokens.colorBrandBackground,
44
- color: tokens.colorNeutralBackground1,
45
- },
46
- stepNumberClickable: {
47
- cursor: "pointer"
48
- },
49
- stepTitle: {
50
- fontSize: tokens.fontSizeBase400,
51
- lineHeight: tokens.lineHeightBase400,
52
- },
53
- progressBar: {
54
- position: "absolute",
55
- top: "14px"
56
- },
57
- stepSpacer: {
58
- position: "relative"
59
- }
60
- });
61
- interface IProps extends DividerProps {
62
- steps: number;
63
- step: number;
64
- stepLabel?: string;
65
- css?: string[];
66
- /** optional, send an icon instead of the step number */
67
- stepIcons?: FluentIcon[];
68
- onStepClick?: (step: number) => void;
69
- }
70
- export const ProgressBarEX = React.forwardRef<HTMLDivElement, (React.PropsWithChildren<IProps>)>((props, ref) => {
71
- const classes = useStyles();
72
-
73
- let stepLabels: JSX.Element[] = [];
74
- for (let i = 0; i < props.steps; i++) {
75
- const stepClasses = [classes.stepNumber];
76
- let addLabel = false;
77
- let canClick = false;
78
- if (props.step === i) {
79
- stepClasses.push(classes.stepNumberCurrent);
80
- if (isNotEmptyString(props.stepLabel))
81
- addLabel = true;
82
- }
83
- else if (props.step > i) {
84
- stepClasses.push(classes.stepNumberCompleted);
85
- canClick = isFunction(props.onStepClick);
86
- if (canClick)
87
- stepClasses.push(classes.stepNumberClickable);
88
- }
89
- let StepIcon = props.stepIcons?.[i];
90
- stepLabels.push(<Section key={`step${i}`} css={stepClasses} onClick={canClick ? () => props.onStepClick(i) : undefined}>{StepIcon ? <StepIcon /> : `${i + 1}`}</Section>);
91
- stepLabels.push(<Section main key={`step${i}Spacer`} css={[classes.stepSpacer]}>
92
- {addLabel && <Horizontal key="label" hCentered css={[classes.stepLabel, KnownClassNames.progressBarStepLabel]}>
93
- <span>{props.stepLabel}</span>
94
- </Horizontal>}
95
- </Section>);
96
-
97
- }
98
-
99
- let StepIcon = props.stepIcons?.[props.steps];
100
- //add last submit step
101
- stepLabels.push(<span key='stepSubmit' className={mergeClasses(classes.stepNumber, props.step === props.steps && classes.stepNumberCompleted)}>{StepIcon ? <StepIcon /> : <CheckmarkRegular />}</span>);
102
-
103
- return (
104
- <Vertical css={[classes.root, ...(props.css || [])]}>
105
- {/* progress bar first so labels will cover it without the need for zindex */}
106
- <ProgressBar className={classes.progressBar} value={(props.step * 2) + 1} max={props.steps * 2} />
107
- <Horizontal css={[classes.stepTitle]}>{...stepLabels}</Horizontal>
108
- </Vertical >
109
- );
1
+ import { DividerProps, makeStyles, mergeClasses, ProgressBar, tokens } from '@fluentui/react-components';
2
+ import { CheckmarkRegular, FluentIcon } from '@fluentui/react-icons';
3
+ import { isFunction, isNotEmptyString } from '@kwiz/common';
4
+ import React from 'react';
5
+ import { KnownClassNames } from '../styles/styles';
6
+ import { Horizontal } from './horizontal';
7
+ import { Section } from './section';
8
+ import { Vertical } from './vertical';
9
+
10
+ const useStyles = makeStyles({
11
+ root: {
12
+ position: "relative"
13
+ },
14
+ stepNumber: {
15
+ border: `2px solid ${tokens.colorNeutralStroke1}`,
16
+ borderRadius: tokens.borderRadiusCircular,
17
+ width: '24px',
18
+ height: '24px',
19
+ position: 'relative',
20
+ display: 'inline-flex',
21
+ alignItems: 'center',
22
+ justifyContent: 'center',
23
+ backgroundColor: tokens.colorNeutralBackground1,
24
+ },
25
+ stepLabel: {
26
+ backgroundColor: tokens.colorNeutralBackground1,
27
+ position: "absolute",
28
+ top: '-10px',
29
+ left: 0,
30
+ right: 0,
31
+ "& > span": {
32
+ whiteSpace: "nowrap",
33
+ overflow: "hidden",
34
+ textOverflow: "ellipsis",
35
+ display: "inline-block"
36
+ }
37
+ },
38
+ stepNumberCurrent: {
39
+ border: `2px solid ${tokens.colorBrandBackground}`,
40
+ },
41
+ stepNumberCompleted: {
42
+ border: `2px solid ${tokens.colorBrandBackground}`,
43
+ backgroundColor: tokens.colorBrandBackground,
44
+ color: tokens.colorNeutralBackground1,
45
+ },
46
+ stepNumberClickable: {
47
+ cursor: "pointer"
48
+ },
49
+ stepTitle: {
50
+ fontSize: tokens.fontSizeBase400,
51
+ lineHeight: tokens.lineHeightBase400,
52
+ },
53
+ progressBar: {
54
+ position: "absolute",
55
+ top: "14px"
56
+ },
57
+ stepSpacer: {
58
+ position: "relative"
59
+ }
60
+ });
61
+ interface IProps extends DividerProps {
62
+ steps: number;
63
+ step: number;
64
+ stepLabel?: string;
65
+ css?: string[];
66
+ /** optional, send an icon instead of the step number */
67
+ stepIcons?: FluentIcon[];
68
+ onStepClick?: (step: number) => void;
69
+ }
70
+ export const ProgressBarEX = React.forwardRef<HTMLDivElement, (React.PropsWithChildren<IProps>)>((props, ref) => {
71
+ const classes = useStyles();
72
+
73
+ let stepLabels: JSX.Element[] = [];
74
+ for (let i = 0; i < props.steps; i++) {
75
+ const stepClasses = [classes.stepNumber];
76
+ let addLabel = false;
77
+ let canClick = false;
78
+ if (props.step === i) {
79
+ stepClasses.push(classes.stepNumberCurrent);
80
+ if (isNotEmptyString(props.stepLabel))
81
+ addLabel = true;
82
+ }
83
+ else if (props.step > i) {
84
+ stepClasses.push(classes.stepNumberCompleted);
85
+ canClick = isFunction(props.onStepClick);
86
+ if (canClick)
87
+ stepClasses.push(classes.stepNumberClickable);
88
+ }
89
+ let StepIcon = props.stepIcons?.[i];
90
+ stepLabels.push(<Section key={`step${i}`} css={stepClasses} onClick={canClick ? () => props.onStepClick(i) : undefined}>{StepIcon ? <StepIcon /> : `${i + 1}`}</Section>);
91
+ stepLabels.push(<Section main key={`step${i}Spacer`} css={[classes.stepSpacer]}>
92
+ {addLabel && <Horizontal key="label" hCentered css={[classes.stepLabel, KnownClassNames.progressBarStepLabel]}>
93
+ <span>{props.stepLabel}</span>
94
+ </Horizontal>}
95
+ </Section>);
96
+
97
+ }
98
+
99
+ let StepIcon = props.stepIcons?.[props.steps];
100
+ //add last submit step
101
+ stepLabels.push(<span key='stepSubmit' className={mergeClasses(classes.stepNumber, props.step === props.steps && classes.stepNumberCompleted)}>{StepIcon ? <StepIcon /> : <CheckmarkRegular />}</span>);
102
+
103
+ return (
104
+ <Vertical css={[classes.root, ...(props.css || [])]}>
105
+ {/* progress bar first so labels will cover it without the need for zindex */}
106
+ <ProgressBar className={classes.progressBar} value={(props.step * 2) + 1} max={props.steps * 2} />
107
+ <Horizontal css={[classes.stepTitle]}>{...stepLabels}</Horizontal>
108
+ </Vertical >
109
+ );
110
110
  });