@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,41 +1,41 @@
1
- import { Field, mergeClasses, Textarea } from '@fluentui/react-components';
2
- import { isNullOrUndefined } from '@kwiz/common';
3
- import React from 'react';
4
- import { GetLogger } from '../_modules/config';
5
- import { InputEx } from './input';
6
-
7
- const logger = GetLogger('FieldEditor');
8
-
9
- interface IProps {
10
- required?: boolean;
11
- error?: string;
12
- value: string;
13
- onChange: (newValue: string) => void;
14
- css: string[];
15
- label: string;
16
- description?: string;
17
- type?: "text" | "multiline"
18
- }
19
- export const FieldEditor: React.FunctionComponent<IProps> = (props) => {
20
- if (isNullOrUndefined(props.value)) {
21
- logger.error(`${props.label}: value should not be null`);
22
- }
23
- return (
24
- <Field required={props.required}
25
- validationMessage={props.error || props.description}
26
- validationState={props.error ? "error" : "none"}>
27
- {props.type === "multiline"
28
- ? <Textarea className={props.css && mergeClasses(...props.css)}
29
- required={props.required}
30
- placeholder={props.label}
31
- value={props.value || ""}
32
- onChange={(e, data) => props.onChange(data.value)}
33
- />
34
- : <InputEx className={props.css && mergeClasses(...props.css)}
35
- required={props.required}
36
- placeholder={props.label}
37
- value={props.value || ""}
38
- onChange={(e, data) => props.onChange(data.value)} />}
39
- </Field>
40
- );
1
+ import { Field, mergeClasses, Textarea } from '@fluentui/react-components';
2
+ import { isNullOrUndefined } from '@kwiz/common';
3
+ import React from 'react';
4
+ import { GetLogger } from '../_modules/config';
5
+ import { InputEx } from './input';
6
+
7
+ const logger = GetLogger('FieldEditor');
8
+
9
+ interface IProps {
10
+ required?: boolean;
11
+ error?: string;
12
+ value: string;
13
+ onChange: (newValue: string) => void;
14
+ css: string[];
15
+ label: string;
16
+ description?: string;
17
+ type?: "text" | "multiline"
18
+ }
19
+ export const FieldEditor: React.FunctionComponent<IProps> = (props) => {
20
+ if (isNullOrUndefined(props.value)) {
21
+ logger.error(`${props.label}: value should not be null`);
22
+ }
23
+ return (
24
+ <Field required={props.required}
25
+ validationMessage={props.error || props.description}
26
+ validationState={props.error ? "error" : "none"}>
27
+ {props.type === "multiline"
28
+ ? <Textarea className={props.css && mergeClasses(...props.css)}
29
+ required={props.required}
30
+ placeholder={props.label}
31
+ value={props.value || ""}
32
+ onChange={(e, data) => props.onChange(data.value)}
33
+ />
34
+ : <InputEx className={props.css && mergeClasses(...props.css)}
35
+ required={props.required}
36
+ placeholder={props.label}
37
+ value={props.value || ""}
38
+ onChange={(e, data) => props.onChange(data.value)} />}
39
+ </Field>
40
+ );
41
41
  }
@@ -1,68 +1,68 @@
1
- import { ButtonProps } from "@fluentui/react-components";
2
- import { isFunction, isNotEmptyArray, isNullOrEmptyString } from '@kwiz/common';
3
- import * as React from "react";
4
- import { ButtonEX, CompoundButtonEXSecondary } from "./button";
5
-
6
- interface iProps {
7
- showTitleWithIcon?: boolean;
8
- title?: string;
9
- /** Passing this will turn the button into a compound button */
10
- secondaryContent?: string;
11
- limitFileTypes?: string[];
12
- allowMultiple?: boolean;
13
- icon?: JSX.Element;
14
- onChange?: (newFile: File | FileList) => void;
15
- /** only works for single file, reads it as base64 */
16
- asBase64?: (base64: string) => void;
17
- buttonProps?: ButtonProps;
18
- disabled?: boolean;
19
- }
20
-
21
- export const FileUpload = React.forwardRef<HTMLButtonElement, (iProps)>((props, ref) => {
22
- const hiddenFileInput = React.useRef(null);
23
- const isMulti = props.allowMultiple === true;
24
- return <>
25
- {isNullOrEmptyString(props.secondaryContent)
26
- ? <ButtonEX ref={ref} {...(props.buttonProps || {})} icon={props.icon} showTitleWithIcon={props.showTitleWithIcon} onClick={() => {
27
- hiddenFileInput.current.value = "";
28
- hiddenFileInput.current.click();
29
- }} title={props.title}
30
- disabled={props.disabled}
31
- />
32
- : <CompoundButtonEXSecondary ref={ref} {...(props.buttonProps || {})} icon={props.icon}
33
- secondaryContent={props.secondaryContent}
34
- onClick={() => {
35
- hiddenFileInput.current.value = "";
36
- hiddenFileInput.current.click();
37
- }} title={props.title}
38
- disabled={props.disabled}
39
- />}
40
- <input type="file" ref={hiddenFileInput} style={{ display: "none" }} multiple={isMulti}
41
- accept={isNotEmptyArray(props.limitFileTypes) ? props.limitFileTypes.map(ft => `.${ft}`).join() : undefined}
42
- onChange={(e) => {
43
- if (e.target.files && e.target.files.length > 0) {
44
- if (isMulti) {
45
- if (isFunction(props.onChange)) {
46
- props.onChange(e.target.files);
47
- }
48
- }
49
- else {
50
- const fileUploaded = e.target.files && e.target.files[0];
51
- if (isFunction(props.onChange)) {
52
- props.onChange(fileUploaded);
53
- }
54
- if (isFunction(props.asBase64) && fileUploaded) {
55
- const reader = new FileReader();
56
- reader.onloadend = () => {
57
- console.log(reader.result);
58
- if (!isNullOrEmptyString(reader.result))
59
- props.asBase64(reader.result as string);
60
- };
61
- reader.readAsDataURL(fileUploaded);
62
- }
63
- }
64
- }
65
- }}
66
- />
67
- </>;
1
+ import { ButtonProps } from "@fluentui/react-components";
2
+ import { isFunction, isNotEmptyArray, isNullOrEmptyString } from '@kwiz/common';
3
+ import * as React from "react";
4
+ import { ButtonEX, CompoundButtonEXSecondary } from "./button";
5
+
6
+ interface iProps {
7
+ showTitleWithIcon?: boolean;
8
+ title?: string;
9
+ /** Passing this will turn the button into a compound button */
10
+ secondaryContent?: string;
11
+ limitFileTypes?: string[];
12
+ allowMultiple?: boolean;
13
+ icon?: JSX.Element;
14
+ onChange?: (newFile: File | FileList) => void;
15
+ /** only works for single file, reads it as base64 */
16
+ asBase64?: (base64: string) => void;
17
+ buttonProps?: ButtonProps;
18
+ disabled?: boolean;
19
+ }
20
+
21
+ export const FileUpload = React.forwardRef<HTMLButtonElement, (iProps)>((props, ref) => {
22
+ const hiddenFileInput = React.useRef(null);
23
+ const isMulti = props.allowMultiple === true;
24
+ return <>
25
+ {isNullOrEmptyString(props.secondaryContent)
26
+ ? <ButtonEX ref={ref} {...(props.buttonProps || {})} icon={props.icon} showTitleWithIcon={props.showTitleWithIcon} onClick={() => {
27
+ hiddenFileInput.current.value = "";
28
+ hiddenFileInput.current.click();
29
+ }} title={props.title}
30
+ disabled={props.disabled}
31
+ />
32
+ : <CompoundButtonEXSecondary ref={ref} {...(props.buttonProps || {})} icon={props.icon}
33
+ secondaryContent={props.secondaryContent}
34
+ onClick={() => {
35
+ hiddenFileInput.current.value = "";
36
+ hiddenFileInput.current.click();
37
+ }} title={props.title}
38
+ disabled={props.disabled}
39
+ />}
40
+ <input type="file" ref={hiddenFileInput} style={{ display: "none" }} multiple={isMulti}
41
+ accept={isNotEmptyArray(props.limitFileTypes) ? props.limitFileTypes.map(ft => `.${ft}`).join() : undefined}
42
+ onChange={(e) => {
43
+ if (e.target.files && e.target.files.length > 0) {
44
+ if (isMulti) {
45
+ if (isFunction(props.onChange)) {
46
+ props.onChange(e.target.files);
47
+ }
48
+ }
49
+ else {
50
+ const fileUploaded = e.target.files && e.target.files[0];
51
+ if (isFunction(props.onChange)) {
52
+ props.onChange(fileUploaded);
53
+ }
54
+ if (isFunction(props.asBase64) && fileUploaded) {
55
+ const reader = new FileReader();
56
+ reader.onloadend = () => {
57
+ console.log(reader.result);
58
+ if (!isNullOrEmptyString(reader.result))
59
+ props.asBase64(reader.result as string);
60
+ };
61
+ reader.readAsDataURL(fileUploaded);
62
+ }
63
+ }
64
+ }
65
+ }}
66
+ />
67
+ </>;
68
68
  });
@@ -1,35 +1,35 @@
1
- import { makeStyles } from '@fluentui/react-components';
2
- import { isNotEmptyArray } from '@kwiz/common';
3
- import React from 'react';
4
- import { KnownClassNames, mixins } from '../styles/styles';
5
- import { ISectionProps, Section } from './section';
6
-
7
- const useStyles = makeStyles({
8
- horizontal: {
9
- ...mixins.flex,
10
- flexDirection: 'row'
11
- },
12
- wrap: mixins.wrap,
13
- nogap: mixins.nogap
14
- })
15
-
16
- interface IProps extends ISectionProps {
17
- wrap?: boolean;
18
- nogap?: boolean;
19
- }
20
- export const Horizontal: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
21
- const cssNames = useStyles();
22
- let css: string[] = [KnownClassNames.horizontal];
23
-
24
- css.push(cssNames.horizontal);
25
- if (props.wrap)
26
- css.push(cssNames.wrap);
27
- if (props.nogap)
28
- css.push(cssNames.nogap);
29
-
30
- if (isNotEmptyArray(props.css)) css.push(...props.css);
31
-
32
- return (
33
- <Section {...props} css={css} />
34
- );
1
+ import { makeStyles } from '@fluentui/react-components';
2
+ import { isNotEmptyArray } from '@kwiz/common';
3
+ import React from 'react';
4
+ import { KnownClassNames, mixins } from '../styles/styles';
5
+ import { ISectionProps, Section } from './section';
6
+
7
+ const useStyles = makeStyles({
8
+ horizontal: {
9
+ ...mixins.flex,
10
+ flexDirection: 'row'
11
+ },
12
+ wrap: mixins.wrap,
13
+ nogap: mixins.nogap
14
+ })
15
+
16
+ interface IProps extends ISectionProps {
17
+ wrap?: boolean;
18
+ nogap?: boolean;
19
+ }
20
+ export const Horizontal: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
21
+ const cssNames = useStyles();
22
+ let css: string[] = [KnownClassNames.horizontal];
23
+
24
+ css.push(cssNames.horizontal);
25
+ if (props.wrap)
26
+ css.push(cssNames.wrap);
27
+ if (props.nogap)
28
+ css.push(cssNames.nogap);
29
+
30
+ if (isNotEmptyArray(props.css)) css.push(...props.css);
31
+
32
+ return (
33
+ <Section {...props} css={css} />
34
+ );
35
35
  }
@@ -1,61 +1,61 @@
1
- import { GriffelStyle, Input, InputProps, makeStyles, mergeClasses, Textarea, TextareaProps } from '@fluentui/react-components';
2
- import { isFunction } from '@kwiz/common';
3
- import React from 'react';
4
- import { useKWIZFluentContext } from '../helpers/context';
5
-
6
-
7
- interface IProps extends InputProps {
8
- onOK?: () => void;
9
- onCancel?: () => void;
10
- }
11
- export const InputEx: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
12
- const ctx = useKWIZFluentContext();
13
- return (
14
- <Input appearance={ctx.inputAppearance} {...props}
15
- onKeyDown={isFunction(props.onOK) || isFunction(props.onCancel)
16
- ? e => {
17
- if (isFunction(props.onOK) && e.key === "Enter") props.onOK();
18
- else if (isFunction(props.onCancel) && e.key === "Escape") props.onCancel();
19
- }
20
- : undefined
21
- }
22
- />
23
- );
24
- }
25
-
26
- const fullSize: GriffelStyle = {
27
- width: '100% !important',
28
- maxHeight: '100% !important'
29
- };
30
- const useStyles = makeStyles({
31
- fullSizeTextArea: {
32
- ...fullSize,
33
- ['& > textarea']: fullSize
34
- },
35
- })
36
-
37
- interface IPropsTextArea extends TextareaProps {
38
- fullSize?: boolean;
39
- growNoShrink?: boolean;
40
- }
41
- export const TextAreaEx: React.FunctionComponent<React.PropsWithChildren<IPropsTextArea>> = (props) => {
42
- const cssNames = useStyles();
43
- let css: string[] = [];
44
-
45
- if (props.fullSize) css.push(cssNames.fullSizeTextArea);
46
- const textAreaRef = React.useRef<HTMLTextAreaElement>(null);
47
- const recalcHeight = React.useCallback(() => {
48
- if (textAreaRef.current && props.growNoShrink) {
49
- if (textAreaRef.current.scrollHeight > textAreaRef.current.clientHeight)
50
- textAreaRef.current.style.minHeight = textAreaRef.current.scrollHeight + 'px';
51
- }
52
- }, [textAreaRef]);
53
-
54
- let style: React.CSSProperties = { height: '100%', ...props.style };
55
- return (
56
- <Textarea ref={textAreaRef} className={mergeClasses(...css)} {...props} style={style} onChange={(e, d) => {
57
- if (props.onChange) props.onChange(e, d);
58
- recalcHeight();
59
- }} />
60
- );
1
+ import { GriffelStyle, Input, InputProps, makeStyles, mergeClasses, Textarea, TextareaProps } from '@fluentui/react-components';
2
+ import { isFunction } from '@kwiz/common';
3
+ import React from 'react';
4
+ import { useKWIZFluentContext } from '../helpers/context';
5
+
6
+
7
+ interface IProps extends InputProps {
8
+ onOK?: () => void;
9
+ onCancel?: () => void;
10
+ }
11
+ export const InputEx: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
12
+ const ctx = useKWIZFluentContext();
13
+ return (
14
+ <Input appearance={ctx.inputAppearance} {...props}
15
+ onKeyDown={isFunction(props.onOK) || isFunction(props.onCancel)
16
+ ? e => {
17
+ if (isFunction(props.onOK) && e.key === "Enter") props.onOK();
18
+ else if (isFunction(props.onCancel) && e.key === "Escape") props.onCancel();
19
+ }
20
+ : undefined
21
+ }
22
+ />
23
+ );
24
+ }
25
+
26
+ const fullSize: GriffelStyle = {
27
+ width: '100% !important',
28
+ maxHeight: '100% !important'
29
+ };
30
+ const useStyles = makeStyles({
31
+ fullSizeTextArea: {
32
+ ...fullSize,
33
+ ['& > textarea']: fullSize
34
+ },
35
+ })
36
+
37
+ interface IPropsTextArea extends TextareaProps {
38
+ fullSize?: boolean;
39
+ growNoShrink?: boolean;
40
+ }
41
+ export const TextAreaEx: React.FunctionComponent<React.PropsWithChildren<IPropsTextArea>> = (props) => {
42
+ const cssNames = useStyles();
43
+ let css: string[] = [];
44
+
45
+ if (props.fullSize) css.push(cssNames.fullSizeTextArea);
46
+ const textAreaRef = React.useRef<HTMLTextAreaElement>(null);
47
+ const recalcHeight = React.useCallback(() => {
48
+ if (textAreaRef.current && props.growNoShrink) {
49
+ if (textAreaRef.current.scrollHeight > textAreaRef.current.clientHeight)
50
+ textAreaRef.current.style.minHeight = textAreaRef.current.scrollHeight + 'px';
51
+ }
52
+ }, [textAreaRef]);
53
+
54
+ let style: React.CSSProperties = { height: '100%', ...props.style };
55
+ return (
56
+ <Textarea ref={textAreaRef} className={mergeClasses(...css)} {...props} style={style} onChange={(e, d) => {
57
+ if (props.onChange) props.onChange(e, d);
58
+ recalcHeight();
59
+ }} />
60
+ );
61
61
  }
@@ -1,103 +1,104 @@
1
- import {
2
- Menu, MenuButton, MenuItem, MenuList, MenuPopover, MenuTrigger, Overflow, OverflowItem,
3
- useIsOverflowItemVisible, useOverflowMenu
4
- } from "@fluentui/react-components";
5
- import { isNumber } from '@kwiz/common';
6
- import { MoreHorizontalFilled } from "@fluentui/react-icons";
7
-
8
- interface IProps<ItemType> {
9
- items: ItemType[];
10
- getKey: (item: ItemType, index: number) => string;
11
- getPriority?: (item: ItemType, index: number) => number;
12
- renderItem: (item: ItemType, index: number, overflow?: boolean) => JSX.Element;
13
- groupWrapper?: (children: React.ReactNode) => JSX.Element;
14
- menuRef?: React.RefObject<HTMLButtonElement>;
15
- menuWrapper?: (children: React.ReactNode) => JSX.Element;
16
- menuTrigger?: (ref: React.RefObject<HTMLButtonElement>, overflowCount: number) => JSX.Element;
17
- className?: string;
18
- }
19
- const OverflowMenu = <ItemType,>(props: IProps<ItemType>) => {
20
- const { ref, isOverflowing, overflowCount } =
21
- useOverflowMenu<HTMLButtonElement>();
22
-
23
- if (!isOverflowing) {
24
- return null;
25
- }
26
-
27
- let menu = <Menu>
28
- <MenuTrigger disableButtonEnhancement>
29
- {props.menuTrigger
30
- ? props.menuTrigger(props.menuRef || ref, overflowCount)
31
- : <MenuButton
32
- icon={<MoreHorizontalFilled/>}
33
- ref={props.menuRef || ref}
34
- aria-label="More items"
35
- appearance="subtle"
36
- />}
37
- </MenuTrigger>
38
- <MenuPopover>
39
- <MenuList>
40
- {props.items.map((item, index) => (
41
- <OverflowMenuItem key={props.getKey(item, index)} {...props} item={item} index={index} />
42
- ))}
43
- </MenuList>
44
- </MenuPopover>
45
- </Menu>;
46
-
47
- return (
48
- props.menuWrapper
49
- ? props.menuWrapper(menu)
50
- : menu
51
- );
52
- }
53
-
54
- const OverflowMenuItem = <ItemType,>(props: IProps<ItemType> & { item: ItemType, index: number }) => {
55
- const isVisible = useIsOverflowItemVisible(props.getKey(props.item, props.index));
56
-
57
- if (isVisible) {
58
- return null;
59
- }
60
-
61
- return (
62
- <MenuItem key={props.getKey(props.item, props.index)}>
63
- {props.renderItem(props.item, props.index, true)}
64
- </MenuItem>
65
- );
66
- };
67
- export const KWIZOverflow = <ItemType,>(props: IProps<ItemType>) => {
68
- let content: JSX.Element[] = [];
69
- let addMenu = () => {
70
- if (menuIndex >= 0)
71
- content.splice(menuIndex, 0, <OverflowMenu key="overflow_menu" {...props} />);
72
- else
73
- content.push(<OverflowMenu key="overflow_menu" {...props} />);
74
- };
75
-
76
- let menuIndex = -1;
77
-
78
- props.items.forEach((item, index) => {
79
- //add the menu before the first item with priority
80
- let priority = props.getPriority ? props.getPriority(item, index) : undefined;
81
- if (isNumber(priority) && priority > 0)
82
- menuIndex = index;
83
-
84
- content.push(<OverflowItem key={props.getKey(item, index)} id={props.getKey(item, index)}
85
- priority={priority}>
86
- {props.renderItem(item, index)}
87
- </OverflowItem>);
88
- });
89
-
90
- addMenu();
91
-
92
- return (
93
- <Overflow minimumVisible={2} padding={60} key={`overflow_${props.items.length}`}>
94
- <div style={{ overflow: "hidden" }} className={props.className}>
95
- {
96
- props.groupWrapper
97
- ? props.groupWrapper(content)
98
- : content
99
- }
100
- </div>
101
- </Overflow>
102
- )
1
+ import {
2
+ Menu, MenuButton, MenuItem, MenuList, MenuPopover, MenuTrigger, Overflow, OverflowItem,
3
+ useIsOverflowItemVisible, useOverflowMenu
4
+ } from "@fluentui/react-components";
5
+ import { MoreHorizontalFilled } from "@fluentui/react-icons";
6
+ import { isNumber } from '@kwiz/common';
7
+
8
+ interface IProps<ItemType> {
9
+ /** you cannot have a menu with trigger in overflow items. put those in groupWrapper controls before/after rendering children. */
10
+ items: ItemType[];
11
+ getKey: (item: ItemType, index: number) => string;
12
+ getPriority?: (item: ItemType, index: number) => number;
13
+ renderItem: (item: ItemType, index: number, overflow?: boolean) => JSX.Element;
14
+ groupWrapper?: (children: React.ReactNode) => JSX.Element;
15
+ menuRef?: React.RefObject<HTMLButtonElement>;
16
+ menuWrapper?: (children: React.ReactNode) => JSX.Element;
17
+ menuTrigger?: (ref: React.RefObject<HTMLButtonElement>, overflowCount: number) => JSX.Element;
18
+ className?: string;
19
+ }
20
+ const OverflowMenu = <ItemType,>(props: IProps<ItemType>) => {
21
+ const { ref, isOverflowing, overflowCount } =
22
+ useOverflowMenu<HTMLButtonElement>();
23
+
24
+ if (!isOverflowing) {
25
+ return null;
26
+ }
27
+
28
+ let menu = <Menu>
29
+ <MenuTrigger disableButtonEnhancement>
30
+ {props.menuTrigger
31
+ ? props.menuTrigger(props.menuRef || ref, overflowCount)
32
+ : <MenuButton
33
+ icon={<MoreHorizontalFilled/>}
34
+ ref={props.menuRef || ref}
35
+ aria-label="More items"
36
+ appearance="subtle"
37
+ />}
38
+ </MenuTrigger>
39
+ <MenuPopover>
40
+ <MenuList>
41
+ {props.items.map((item, index) => (
42
+ <OverflowMenuItem key={props.getKey(item, index)} {...props} item={item} index={index} />
43
+ ))}
44
+ </MenuList>
45
+ </MenuPopover>
46
+ </Menu>;
47
+
48
+ return (
49
+ props.menuWrapper
50
+ ? props.menuWrapper(menu)
51
+ : menu
52
+ );
53
+ }
54
+
55
+ const OverflowMenuItem = <ItemType,>(props: IProps<ItemType> & { item: ItemType, index: number }) => {
56
+ const isVisible = useIsOverflowItemVisible(props.getKey(props.item, props.index));
57
+
58
+ if (isVisible) {
59
+ return null;
60
+ }
61
+
62
+ return (
63
+ <MenuItem key={props.getKey(props.item, props.index)}>
64
+ {props.renderItem(props.item, props.index, true)}
65
+ </MenuItem>
66
+ );
67
+ };
68
+ export const KWIZOverflow = <ItemType,>(props: IProps<ItemType>) => {
69
+ let content: JSX.Element[] = [];
70
+ let addMenu = () => {
71
+ if (menuIndex >= 0)
72
+ content.splice(menuIndex, 0, <OverflowMenu key="overflow_menu" {...props} />);
73
+ else
74
+ content.push(<OverflowMenu key="overflow_menu" {...props} />);
75
+ };
76
+
77
+ let menuIndex = -1;
78
+
79
+ props.items.forEach((item, index) => {
80
+ //add the menu before the first item with priority
81
+ let priority = props.getPriority ? props.getPriority(item, index) : undefined;
82
+ if (isNumber(priority) && priority > 0)
83
+ menuIndex = index;
84
+
85
+ content.push(<OverflowItem key={props.getKey(item, index)} id={props.getKey(item, index)}
86
+ priority={priority}>
87
+ {props.renderItem(item, index)}
88
+ </OverflowItem>);
89
+ });
90
+
91
+ addMenu();
92
+
93
+ return (
94
+ <Overflow minimumVisible={2} padding={60} key={`overflow_${props.items.length}`}>
95
+ <div style={{ overflow: "hidden" }} className={props.className}>
96
+ {
97
+ props.groupWrapper
98
+ ? props.groupWrapper(content)
99
+ : content
100
+ }
101
+ </div>
102
+ </Overflow>
103
+ )
103
104
  };