@kwiz/fluentui 1.0.0

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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/_modules/build.d.ts +2 -0
  4. package/dist/_modules/build.js +3 -0
  5. package/dist/_modules/build.js.map +1 -0
  6. package/dist/_modules/config.d.ts +1 -0
  7. package/dist/_modules/config.js +9 -0
  8. package/dist/_modules/config.js.map +1 -0
  9. package/dist/_modules/constants.d.ts +2 -0
  10. package/dist/_modules/constants.js +3 -0
  11. package/dist/_modules/constants.js.map +1 -0
  12. package/dist/_modules/exports-index.d.ts +1 -0
  13. package/dist/_modules/exports-index.js +2 -0
  14. package/dist/_modules/exports-index.js.map +1 -0
  15. package/dist/controls/button.d.ts +27 -0
  16. package/dist/controls/button.js +100 -0
  17. package/dist/controls/button.js.map +1 -0
  18. package/dist/controls/centered.d.ts +5 -0
  19. package/dist/controls/centered.js +14 -0
  20. package/dist/controls/centered.js.map +1 -0
  21. package/dist/controls/dropdown.d.ts +18 -0
  22. package/dist/controls/dropdown.js +13 -0
  23. package/dist/controls/dropdown.js.map +1 -0
  24. package/dist/controls/error-boundary.d.ts +23 -0
  25. package/dist/controls/error-boundary.js +33 -0
  26. package/dist/controls/error-boundary.js.map +1 -0
  27. package/dist/controls/exports-index.d.ts +17 -0
  28. package/dist/controls/exports-index.js +18 -0
  29. package/dist/controls/exports-index.js.map +1 -0
  30. package/dist/controls/field-editor.d.ts +13 -0
  31. package/dist/controls/field-editor.js +15 -0
  32. package/dist/controls/field-editor.js.map +1 -0
  33. package/dist/controls/file-upload.d.ts +18 -0
  34. package/dist/controls/file-upload.js +41 -0
  35. package/dist/controls/file-upload.js.map +1 -0
  36. package/dist/controls/horizontal.d.ts +8 -0
  37. package/dist/controls/horizontal.js +23 -0
  38. package/dist/controls/horizontal.js.map +1 -0
  39. package/dist/controls/input.d.ts +13 -0
  40. package/dist/controls/input.js +41 -0
  41. package/dist/controls/input.js.map +1 -0
  42. package/dist/controls/list.d.ts +21 -0
  43. package/dist/controls/list.js +72 -0
  44. package/dist/controls/list.js.map +1 -0
  45. package/dist/controls/loading.d.ts +5 -0
  46. package/dist/controls/loading.js +7 -0
  47. package/dist/controls/loading.js.map +1 -0
  48. package/dist/controls/please-wait.d.ts +17 -0
  49. package/dist/controls/please-wait.js +16 -0
  50. package/dist/controls/please-wait.js.map +1 -0
  51. package/dist/controls/prompt.d.ts +16 -0
  52. package/dist/controls/prompt.js +21 -0
  53. package/dist/controls/prompt.js.map +1 -0
  54. package/dist/controls/search.d.ts +13 -0
  55. package/dist/controls/search.js +47 -0
  56. package/dist/controls/search.js.map +1 -0
  57. package/dist/controls/section.d.ts +14 -0
  58. package/dist/controls/section.js +27 -0
  59. package/dist/controls/section.js.map +1 -0
  60. package/dist/controls/svg.d.ts +23 -0
  61. package/dist/controls/svg.js +45 -0
  62. package/dist/controls/svg.js.map +1 -0
  63. package/dist/controls/vertical-content.d.ts +6 -0
  64. package/dist/controls/vertical-content.js +37 -0
  65. package/dist/controls/vertical-content.js.map +1 -0
  66. package/dist/controls/vertical.d.ts +8 -0
  67. package/dist/controls/vertical.js +23 -0
  68. package/dist/controls/vertical.js.map +1 -0
  69. package/dist/exports-index.d.ts +3 -0
  70. package/dist/exports-index.js +4 -0
  71. package/dist/exports-index.js.map +1 -0
  72. package/dist/helpers/hooks.d.ts +22 -0
  73. package/dist/helpers/hooks.js +173 -0
  74. package/dist/helpers/hooks.js.map +1 -0
  75. package/dist/index.d.ts +19 -0
  76. package/dist/index.js +20 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/styles/exports-index.d.ts +2 -0
  79. package/dist/styles/exports-index.js +3 -0
  80. package/dist/styles/exports-index.js.map +1 -0
  81. package/dist/styles/styles.d.ts +19 -0
  82. package/dist/styles/styles.js +79 -0
  83. package/dist/styles/styles.js.map +1 -0
  84. package/dist/styles/theme.d.ts +6 -0
  85. package/dist/styles/theme.js +77 -0
  86. package/dist/styles/theme.js.map +1 -0
  87. package/package.json +71 -0
  88. package/src/_modules/config.ts +9 -0
  89. package/src/_modules/constants.ts +3 -0
  90. package/src/controls/button.tsx +154 -0
  91. package/src/controls/centered.tsx +23 -0
  92. package/src/controls/dropdown.tsx +28 -0
  93. package/src/controls/error-boundary.tsx +42 -0
  94. package/src/controls/field-editor.tsx +42 -0
  95. package/src/controls/file-upload.tsx +68 -0
  96. package/src/controls/horizontal.tsx +35 -0
  97. package/src/controls/input.tsx +59 -0
  98. package/src/controls/list.tsx +118 -0
  99. package/src/controls/loading.tsx +11 -0
  100. package/src/controls/please-wait.tsx +32 -0
  101. package/src/controls/prompt.tsx +59 -0
  102. package/src/controls/search.tsx +66 -0
  103. package/src/controls/section.tsx +52 -0
  104. package/src/controls/svg.tsx +120 -0
  105. package/src/controls/vertical-content.tsx +50 -0
  106. package/src/controls/vertical.tsx +35 -0
  107. package/src/helpers/hooks.ts +189 -0
  108. package/src/index.ts +19 -0
  109. package/src/styles/styles.ts +87 -0
  110. package/src/styles/theme.ts +91 -0
@@ -0,0 +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
+ );
66
+ }
@@ -0,0 +1,52 @@
1
+ import { makeStyles, mergeClasses, tokens } from '@fluentui/react-components';
2
+ import { isFunction, isNotEmptyArray } from '@kwiz/common';
3
+ import React from 'react';
4
+ import { KnownClassNames, mixins } from '../styles/styles';
5
+
6
+ const useStyles = makeStyles({
7
+ main: mixins.main,
8
+ clickable: mixins.clickable,
9
+ left: {
10
+ ...mixins.float,
11
+ float: "left",
12
+ marginRight: tokens.spacingHorizontalXXL
13
+ },
14
+ right: {
15
+ ...mixins.float,
16
+ float: "right",
17
+ marginRight: tokens.spacingHorizontalXXL
18
+ }
19
+ });
20
+
21
+
22
+ export interface ISectionProps {
23
+ main?: boolean;
24
+ css?: string[];
25
+ style?: React.CSSProperties;
26
+ onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
27
+ rootProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
28
+ title?: string;
29
+ left?: boolean;
30
+ right?: boolean;
31
+ }
32
+
33
+ export const Section = React.forwardRef<HTMLDivElement, React.PropsWithChildren<ISectionProps>>((props, ref) => {
34
+ const cssNames = useStyles();
35
+ let css: string[] = [KnownClassNames.section];
36
+ if (props.main) css.push(cssNames.main);
37
+ if (isFunction(props.onClick))
38
+ css.push(cssNames.clickable);
39
+
40
+ if (props.left) css.push(cssNames.left);
41
+ else if (props.right) css.push(cssNames.right);
42
+
43
+ if (isNotEmptyArray(props.css)) css.push(...props.css);
44
+
45
+ return (
46
+ <div ref={ref} {...(props.rootProps || {})} title={props.title} style={props.style}
47
+ className={mergeClasses(...css)}
48
+ onClick={props.onClick}>
49
+ {props.children}
50
+ </div>
51
+ );
52
+ });
@@ -0,0 +1,120 @@
1
+ import { tokens } from '@fluentui/react-components';
2
+ import React from 'react';
3
+
4
+ interface IProps {
5
+ size: number;
6
+ }
7
+ export const YouTubeIcon: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
8
+ return (
9
+ <svg height={`${props.size}px`} width={`${props.size}px`} version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink"
10
+ viewBox="0 0 461.001 461.001" xmlSpace="preserve">
11
+ <g>
12
+ <path style={{ fill: "#F61C0D" }} d="M365.257,67.393H95.744C42.866,67.393,0,110.259,0,163.137v134.728
13
+ c0,52.878,42.866,95.744,95.744,95.744h269.513c52.878,0,95.744-42.866,95.744-95.744V163.137
14
+ C461.001,110.259,418.135,67.393,365.257,67.393z M300.506,237.056l-126.06,60.123c-3.359,1.602-7.239-0.847-7.239-4.568V168.607
15
+ c0-3.774,3.982-6.22,7.348-4.514l126.06,63.881C304.363,229.873,304.298,235.248,300.506,237.056z"/>
16
+ </g>
17
+ </svg>
18
+ );
19
+ }
20
+
21
+ export const MermaidIcon: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
22
+ return (
23
+ <svg height={`${props.size}px`} width={`${props.size}px`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490.16 490.16">
24
+ <defs>
25
+ <style>{`.mm-cls-1{fill:#ff3670;}.mm-cls-2{fill:#fff;}`}</style></defs>
26
+ <rect className="mm-cls-1" width="490.16" height="490.16" rx="84.61" />
27
+ <path className="mm-cls-2" d="M407.48,111.18A165.2,165.2,0,0,0,245.08,220,165.2,165.2,0,0,0,82.68,111.18a165.5,165.5,0,0,0,72.06,143.64,88.81,88.81,0,0,1,38.53,73.45v50.86H296.9V328.27a88.8,88.8,0,0,1,38.52-73.45,165.41,165.41,0,0,0,72.06-143.64Z" />
28
+ <path className="mm-cls-2" d="M160.63,328.27a56.09,56.09,0,0,0-24.27-46.49,198.74,198.74,0,0,1-28.54-23.66A196.87,196.87,0,0,1,82.53,227V379.13h78.1Z" />
29
+ <path className="mm-cls-2" d="M329.53,328.27a56.09,56.09,0,0,1,24.27-46.49,198.74,198.74,0,0,0,28.54-23.66A196.87,196.87,0,0,0,407.63,227V379.13h-78.1Z" />
30
+ </svg>
31
+ );
32
+ }
33
+
34
+ export const TeamsIcon: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
35
+ return (
36
+ <svg height={`${props.size}px`} width={`${props.size}px`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2228.833 2073.333">
37
+ <path fill="#5059C9" d="M1554.637,777.5h575.713c54.391,0,98.483,44.092,98.483,98.483c0,0,0,0,0,0v524.398 c0,199.901-162.051,361.952-361.952,361.952h0h-1.711c-199.901,0.028-361.975-162-362.004-361.901c0-0.017,0-0.034,0-0.052V828.971 C1503.167,800.544,1526.211,777.5,1554.637,777.5L1554.637,777.5z" />
38
+ <circle fill="#5059C9" cx="1943.75" cy="440.583" r="233.25" />
39
+ <circle fill="#7B83EB" cx="1218.083" cy="336.917" r="336.917" />
40
+ <path fill="#7B83EB" d="M1667.323,777.5H717.01c-53.743,1.33-96.257,45.931-95.01,99.676v598.105 c-7.505,322.519,247.657,590.16,570.167,598.053c322.51-7.893,577.671-275.534,570.167-598.053V877.176 C1763.579,823.431,1721.066,778.83,1667.323,777.5z" />
41
+ <path opacity=".1" d="M1244,777.5v838.145c-0.258,38.435-23.549,72.964-59.09,87.598 c-11.316,4.787-23.478,7.254-35.765,7.257H667.613c-6.738-17.105-12.958-34.21-18.142-51.833 c-18.144-59.477-27.402-121.307-27.472-183.49V877.02c-1.246-53.659,41.198-98.19,94.855-99.52H1244z" />
42
+ <path opacity=".2" d="M1192.167,777.5v889.978c-0.002,12.287-2.47,24.449-7.257,35.765 c-14.634,35.541-49.163,58.833-87.598,59.09H691.975c-8.812-17.105-17.105-34.21-24.362-51.833 c-7.257-17.623-12.958-34.21-18.142-51.833c-18.144-59.476-27.402-121.307-27.472-183.49V877.02 c-1.246-53.659,41.198-98.19,94.855-99.52H1192.167z" />
43
+ <path opacity=".2" d="M1192.167,777.5v786.312c-0.395,52.223-42.632,94.46-94.855,94.855h-447.84 c-18.144-59.476-27.402-121.307-27.472-183.49V877.02c-1.246-53.659,41.198-98.19,94.855-99.52H1192.167z" />
44
+ <path opacity=".2" d="M1140.333,777.5v786.312c-0.395,52.223-42.632,94.46-94.855,94.855H649.472 c-18.144-59.476-27.402-121.307-27.472-183.49V877.02c-1.246-53.659,41.198-98.19,94.855-99.52H1140.333z" />
45
+ <path opacity=".1" d="M1244,509.522v163.275c-8.812,0.518-17.105,1.037-25.917,1.037 c-8.812,0-17.105-0.518-25.917-1.037c-17.496-1.161-34.848-3.937-51.833-8.293c-104.963-24.857-191.679-98.469-233.25-198.003 c-7.153-16.715-12.706-34.071-16.587-51.833h258.648C1201.449,414.866,1243.801,457.217,1244,509.522z" />
46
+ <path opacity=".2" d="M1192.167,561.355v111.442c-17.496-1.161-34.848-3.937-51.833-8.293 c-104.963-24.857-191.679-98.469-233.25-198.003h190.228C1149.616,466.699,1191.968,509.051,1192.167,561.355z" />
47
+ <path opacity=".2" d="M1192.167,561.355v111.442c-17.496-1.161-34.848-3.937-51.833-8.293 c-104.963-24.857-191.679-98.469-233.25-198.003h190.228C1149.616,466.699,1191.968,509.051,1192.167,561.355z" />
48
+ <path opacity=".2" d="M1140.333,561.355v103.148c-104.963-24.857-191.679-98.469-233.25-198.003 h138.395C1097.783,466.699,1140.134,509.051,1140.333,561.355z" />
49
+ <linearGradient id="a" gradientUnits="userSpaceOnUse" x1="198.099" y1="1683.0726" x2="942.2344" y2="394.2607" gradientTransform="matrix(1 0 0 -1 0 2075.3333)">
50
+ <stop offset="0" stopColor="#5a62c3" />
51
+ <stop offset=".5" stopColor="#4d55bd" />
52
+ <stop offset="1" stopColor="#3940ab" />
53
+ </linearGradient>
54
+ <path fill="url(#a)" d="M95.01,466.5h950.312c52.473,0,95.01,42.538,95.01,95.01v950.312c0,52.473-42.538,95.01-95.01,95.01 H95.01c-52.473,0-95.01-42.538-95.01-95.01V561.51C0,509.038,42.538,466.5,95.01,466.5z" />
55
+ <path fill="#FFF" d="M820.211,828.193H630.241v517.297H509.211V828.193H320.123V727.844h500.088V828.193z" />
56
+ </svg>
57
+ );
58
+ }
59
+
60
+ export const SVGLinkIcon = (props: { size: number }) => {
61
+ return (
62
+ <svg fill={tokens.colorNeutralForeground1} height={`${props.size}px`} width={`${props.size}px`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490.16 490.16">
63
+ <g>
64
+ <g>
65
+ <path d="M409.657,32.474c-43.146-43.146-113.832-43.146-156.978,0l-84.763,84.762c29.07-8.262,60.589-6.12,88.129,6.732
66
+ l44.063-44.064c17.136-17.136,44.982-17.136,62.118,0c17.136,17.136,17.136,44.982,0,62.118l-55.386,55.386l-36.414,36.414
67
+ c-17.136,17.136-44.982,17.136-62.119,0l-47.43,47.43c11.016,11.017,23.868,19.278,37.332,24.48
68
+ c36.415,14.382,78.643,8.874,110.467-16.219c3.06-2.447,6.426-5.201,9.18-8.262l57.222-57.222l34.578-34.578
69
+ C453.109,146.306,453.109,75.926,409.657,32.474z"/>
70
+ <path d="M184.135,320.114l-42.228,42.228c-17.136,17.137-44.982,17.137-62.118,0c-17.136-17.136-17.136-44.981,0-62.118
71
+ l91.8-91.799c17.136-17.136,44.982-17.136,62.119,0l47.43-47.43c-11.016-11.016-23.868-19.278-37.332-24.48
72
+ c-38.25-15.3-83.232-8.262-115.362,20.502c-1.53,1.224-3.06,2.754-4.284,3.978l-91.8,91.799
73
+ c-43.146,43.146-43.146,113.832,0,156.979c43.146,43.146,113.832,43.146,156.978,0l82.927-83.845
74
+ C230.035,335.719,220.243,334.496,184.135,320.114z"/>
75
+ </g>
76
+ </g>
77
+ </svg>
78
+ );
79
+ }
80
+
81
+ export const SVGSplitIcon = (props: { size: number }) => {
82
+ return <svg fill={tokens.colorNeutralForeground1} height={`${props.size}px`} width={`${props.size}px`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 17">
83
+ <path d="M10.646 13.146l0.707 0.707-2.853 2.854-2.854-2.854 0.707-0.707 1.647 1.647v-3.772h1v3.772l1.646-1.647zM8 2.207v3.772h1v-3.772l1.646 1.646 0.707-0.707-2.853-2.853-2.854 2.853 0.707 0.707 1.647-1.646zM0 8v1h17v-1h-17z" />
84
+ </svg>;
85
+ }
86
+
87
+ export const HubSpotIcon = (props: { size: number }) => {
88
+ return <svg fill={tokens.colorNeutralForeground1} height={`${props.size}px`} width={`${props.size}px`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
89
+ <path d="M267.4 211.6c-25.1 23.7-40.8 57.3-40.8 94.6 0 29.3 9.7 56.3 26 78L203.1 434c-4.4-1.6-9.1-2.5-14-2.5-10.8 0-20.9 4.2-28.5 11.8-7.6 7.6-11.8 17.8-11.8 28.6s4.2 20.9 11.8 28.5c7.6 7.6 17.8 11.6 28.5 11.6 10.8 0 20.9-3.9 28.6-11.6 7.6-7.6 11.8-17.8 11.8-28.5 0-4.2-.6-8.2-1.9-12.1l50-50.2c22 16.9 49.4 26.9 79.3 26.9 71.9 0 130-58.3 130-130.2 0-65.2-47.7-119.2-110.2-128.7V116c17.5-7.4 28.2-23.8 28.2-42.9 0-26.1-20.9-47.9-47-47.9S311.2 47 311.2 73.1c0 19.1 10.7 35.5 28.2 42.9v61.2c-15.2 2.1-29.6 6.7-42.7 13.6-27.6-20.9-117.5-85.7-168.9-124.8 1.2-4.4 2-9 2-13.8C129.8 23.4 106.3 0 77.4 0 48.6 0 25.2 23.4 25.2 52.2c0 28.9 23.4 52.3 52.2 52.3 9.8 0 18.9-2.9 26.8-7.6l163.2 114.7zm89.5 163.6c-38.1 0-69-30.9-69-69s30.9-69 69-69 69 30.9 69 69-30.9 69-69 69z" />
90
+ </svg>;
91
+ }
92
+
93
+
94
+ //get icons as html
95
+ export const GetSVGLinkIcon = (props: { size: number }) => {
96
+ return (
97
+ `<svg fill="${tokens.colorNeutralForeground1}" height="${props.size}px" width="${props.size}px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490.16 490.16">
98
+ <g>
99
+ <g>
100
+ <path d="M409.657,32.474c-43.146-43.146-113.832-43.146-156.978,0l-84.763,84.762c29.07-8.262,60.589-6.12,88.129,6.732
101
+ l44.063-44.064c17.136-17.136,44.982-17.136,62.118,0c17.136,17.136,17.136,44.982,0,62.118l-55.386,55.386l-36.414,36.414
102
+ c-17.136,17.136-44.982,17.136-62.119,0l-47.43,47.43c11.016,11.017,23.868,19.278,37.332,24.48
103
+ c36.415,14.382,78.643,8.874,110.467-16.219c3.06-2.447,6.426-5.201,9.18-8.262l57.222-57.222l34.578-34.578
104
+ C453.109,146.306,453.109,75.926,409.657,32.474z"/>
105
+ <path d="M184.135,320.114l-42.228,42.228c-17.136,17.137-44.982,17.137-62.118,0c-17.136-17.136-17.136-44.981,0-62.118
106
+ l91.8-91.799c17.136-17.136,44.982-17.136,62.119,0l47.43-47.43c-11.016-11.016-23.868-19.278-37.332-24.48
107
+ c-38.25-15.3-83.232-8.262-115.362,20.502c-1.53,1.224-3.06,2.754-4.284,3.978l-91.8,91.799
108
+ c-43.146,43.146-43.146,113.832,0,156.979c43.146,43.146,113.832,43.146,156.978,0l82.927-83.845
109
+ C230.035,335.719,220.243,334.496,184.135,320.114z"/>
110
+ </g>
111
+ </g>
112
+ </svg>`
113
+ );
114
+ }
115
+
116
+ export const GetSVGSplitIcon = (props: { size: number }) => {
117
+ return `<svg fill="${tokens.colorNeutralForeground1}" height="${props.size}px" width="${props.size}px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 17">
118
+ <path d="M10.646 13.146l0.707 0.707-2.853 2.854-2.854-2.854 0.707-0.707 1.647 1.647v-3.772h1v3.772l1.646-1.647zM8 2.207v3.772h1v-3.772l1.646 1.646 0.707-0.707-2.853-2.853-2.854 2.853 0.707 0.707 1.647-1.646zM0 8v1h17v-1h-17z" />
119
+ </svg>`;
120
+ }
@@ -0,0 +1,50 @@
1
+ import { makeStyles, mergeClasses } from '@fluentui/react-components';
2
+ import { isNotEmptyArray } from '@kwiz/common';
3
+ import React from 'react';
4
+ import { useWindowSize } from '../helpers/hooks';
5
+
6
+ const useStyles = makeStyles({
7
+ verticalContainer: {
8
+ position: "relative",
9
+ ['& > div']: {
10
+ position: "absolute",
11
+ transform: "rotate(90deg)"
12
+ }
13
+ }
14
+ });
15
+
16
+ interface IProps {
17
+ css?: string[];
18
+ }
19
+ export const VerticalContent: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
20
+ const classes = useStyles();
21
+ let css: string[] = [classes.verticalContainer];
22
+ const size = useWindowSize();
23
+
24
+ let div = React.useRef();
25
+ let rotate = React.useRef();
26
+
27
+ if (isNotEmptyArray(props.css)) css.push(...props.css);
28
+
29
+ React.useEffect(() => {
30
+ if (div.current && rotate.current) {
31
+ let rootDiv = (div.current as HTMLDivElement);
32
+ let rotateDiv = (rotate.current as HTMLDivElement);
33
+ rootDiv.style.height = `${rotateDiv.clientWidth}px`;
34
+ rootDiv.style.width = `${rotateDiv.clientHeight}px`;
35
+ rootDiv.style.minHeight = `${rotateDiv.clientWidth}px`;
36
+ rootDiv.style.minWidth = `${rotateDiv.clientHeight}px`;
37
+
38
+ rotateDiv.style.top = `${(rotateDiv.clientWidth - rotateDiv.clientHeight) / 2}px`;
39
+ rotateDiv.style.left = `-${(rotateDiv.clientWidth - rotateDiv.clientHeight) / 2}px`;
40
+ }
41
+ }, [div, rotate, size.height, size.width]);
42
+
43
+ return (
44
+ <div ref={div} className={mergeClasses(...css)}>
45
+ <div ref={rotate}>
46
+ {props.children}
47
+ </div>
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +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
+ vertical: {
9
+ ...mixins.flex,
10
+ flexDirection: 'column'
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 Vertical: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
21
+ const cssNames = useStyles();
22
+ let css: string[] = [KnownClassNames.vertical];
23
+
24
+ css.push(cssNames.vertical);
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
+ }
@@ -0,0 +1,189 @@
1
+ import { isDebug, isFunction, isNotEmptyArray, isNullOrEmptyString, jsonStringify, LoggerLevel, objectsEqual, wrapFunction } from "@kwiz/common";
2
+ import { MutableRefObject, SetStateAction, useCallback, useEffect, useRef, useState } from "react";
3
+ import { GetLogger } from "../_modules/config";
4
+ import { KnownClassNames } from "../styles/styles";
5
+
6
+ const logger = GetLogger("helpers/hooks");
7
+ /** Empty array ensures that effect is only run on mount */
8
+ export const useEffectOnlyOnMount = [];
9
+
10
+ /** set state on steroids. provide promise callback after render, onChange transformer and automatic skip-set when value not changed */
11
+ export function useStateEX<ValueType>(initialValue: ValueType, options?: {
12
+ onChange?: (newValue: SetStateAction<ValueType>) => SetStateAction<ValueType>;
13
+ //will not set state if value did not change
14
+ skipUpdateIfSame?: boolean;
15
+ //optional, provide a name for better logging
16
+ name?: string;
17
+ }):
18
+ [ValueType, (newValue: SetStateAction<ValueType>) => Promise<ValueType>, MutableRefObject<ValueType>] {
19
+ options = options || {};
20
+ const name = options.name || '';
21
+
22
+ let logger = GetLogger(`useStateWithTrack${isNullOrEmptyString(name) ? '' : ` ${name}`}`);
23
+ logger.setLevel(LoggerLevel.WARN);
24
+
25
+ const [value, setValueInState] = useState(initialValue);
26
+ const currentValue = useRef(value);
27
+
28
+ /** make this a collection in case several callers are awaiting the same propr update */
29
+ const resolveState = useRef<((v: ValueType) => void)[]>([]);
30
+ const isMounted = useRef(false);
31
+
32
+ useEffect(() => {
33
+ isMounted.current = true;
34
+
35
+ return () => {
36
+ isMounted.current = false;
37
+ };
38
+ }, useEffectOnlyOnMount);
39
+
40
+ function resolvePromises() {
41
+ if (isNotEmptyArray(resolveState.current)) {
42
+ let resolvers = resolveState.current.slice();
43
+ resolveState.current = [];//clear
44
+ resolvers.map(r => r(currentValue.current));
45
+ }
46
+ };
47
+ useEffect(() => {
48
+ resolvePromises();
49
+ if (isNotEmptyArray(resolveState.current)) {
50
+ logger.log(`resolved after render`);
51
+ let resolvers = resolveState.current.slice();
52
+ resolveState.current = [];//clear
53
+ resolvers.map(r => r(value));
54
+ }
55
+ }, [value, resolveState.current]);
56
+
57
+ let setValueWithCheck = !options.skipUpdateIfSame ? setValueInState : (newValue: ValueType) => {
58
+ logger.groupSync('conditional value change', log => {
59
+ if (logger.getLevel() === LoggerLevel.VERBOSE) {
60
+ log('old: ' + jsonStringify(currentValue.current));
61
+ log('new: ' + jsonStringify(newValue));
62
+ }
63
+ if (!objectsEqual(newValue as object, currentValue.current as object)) {
64
+ log(`value changed`);
65
+ setValueInState(newValue);
66
+ }
67
+ else {
68
+ log(`value unchanged`);
69
+ resolvePromises();
70
+ }
71
+ });
72
+ }
73
+
74
+
75
+ let setValueWithEvents = wrapFunction(setValueWithCheck, {
76
+ before: newValue => isFunction(options.onChange) ? options.onChange(newValue) : newValue,
77
+ after: newValue => currentValue.current = newValue as ValueType
78
+ });
79
+
80
+ const setValue = useCallback((newState: ValueType) => new Promise<ValueType>(resolve => {
81
+ if (!isMounted.current) {
82
+ //unmounted may never resolve
83
+ logger.log(`resolved without wait`);
84
+ resolve(newState);
85
+ }
86
+ else {
87
+ resolveState.current.push(resolve);
88
+ setValueWithEvents(newState);
89
+ }
90
+ }), []);
91
+
92
+ return [value, setValue, currentValue];
93
+ }
94
+ export function useTrackFocus(props: { onFocus: () => void, onLoseFocus: () => void }) {
95
+ const wrapperDiv = useRef<HTMLDivElement>(null);
96
+ useEffect(() => {
97
+ function focusIn(e: FocusEvent) {
98
+ let elm = e.target as HTMLElement;//document.activeElement;
99
+ if (wrapperDiv.current) {
100
+ while (elm && elm !== wrapperDiv.current) {
101
+ elm = elm.parentElement;
102
+ }
103
+ } else elm = null;
104
+ if (wrapperDiv.current && elm === wrapperDiv.current) props.onFocus();
105
+ else props.onLoseFocus();
106
+ }
107
+
108
+ if (wrapperDiv.current) {
109
+ if (wrapperDiv.current) wrapperDiv.current.tabIndex = 1;
110
+ window.addEventListener("focusin", focusIn);
111
+ // Remove event listener on cleanup
112
+ return () => window.removeEventListener("focusin", focusIn);
113
+ }
114
+ }, [wrapperDiv.current]);
115
+ return wrapperDiv;
116
+ }
117
+ export function useWindowSize() {
118
+ // Initialize state with undefined width/height so server and client renders match
119
+ // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
120
+ const [windowSize, setWindowSize] = useState<{
121
+ width: number,
122
+ height: number
123
+ }>({
124
+ width: undefined,
125
+ height: undefined
126
+ });
127
+ useEffect(() => {
128
+ // Handler to call on window resize
129
+ function handleResize() {
130
+
131
+ // Set window width/height to state
132
+ setWindowSize({
133
+ width: window.innerWidth,
134
+ height: window.innerHeight
135
+ });
136
+ }
137
+ // Add event listener
138
+ window.addEventListener("resize", handleResize);
139
+ // Call handler right away so state gets updated with initial window size
140
+ handleResize();
141
+ // Remove event listener on cleanup
142
+ return () => window.removeEventListener("resize", handleResize);
143
+ }, useEffectOnlyOnMount);
144
+ return windowSize;
145
+ }
146
+ export function useIsInPrint() {
147
+ // Initialize state with false
148
+ const [printMode, setPrintMode] = useState<boolean>(false);
149
+ useEffect(() => {
150
+ function forcePrint(e: KeyboardEvent) {
151
+ if (e.ctrlKey && e.shiftKey && e.altKey) {
152
+ if (e.key.toLocaleLowerCase() === "q") {
153
+ document.body.classList.remove(KnownClassNames.print);
154
+ handlePrint(e, false);
155
+ }
156
+ else {
157
+ console.warn('forced print mode - to exit refresh to ctrl+shift+alt+q');
158
+ document.body.classList.add(KnownClassNames.print);
159
+ handlePrint(e, true);
160
+ }
161
+ }
162
+ }
163
+ // Handler to call on printing
164
+ function handlePrint(e?: Event, force?: boolean) {
165
+ if (force === true) setPrintMode(true);
166
+ else if (window.matchMedia) {
167
+ var mediaQueryList = window.matchMedia('print');
168
+ if (mediaQueryList.matches) {
169
+ setPrintMode(true);
170
+ } else {
171
+ setPrintMode(false);
172
+ }
173
+ }
174
+ }
175
+ // Add event listener
176
+ window.addEventListener("print", handlePrint);
177
+ if (isDebug())
178
+ window.addEventListener("keydown", forcePrint);
179
+ // Call handler right away so state gets updated with initial printing state
180
+ handlePrint();
181
+ // Remove event listener on cleanup
182
+ return () => {
183
+ window.removeEventListener("print", handlePrint);
184
+ if (isDebug())
185
+ window.removeEventListener("keydown", forcePrint);
186
+ };
187
+ }, useEffectOnlyOnMount);
188
+ return printMode;
189
+ }
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from './controls/button';
2
+ export * from './controls/centered';
3
+ export * from './controls/dropdown';
4
+ export * from './controls/error-boundary';
5
+ export * from './controls/field-editor';
6
+ export * from './controls/file-upload';
7
+ export * from './controls/horizontal';
8
+ export * from './controls/input';
9
+ export * from './controls/list';
10
+ export * from './controls/loading';
11
+ export * from './controls/please-wait';
12
+ export * from './controls/prompt';
13
+ export * from './controls/search';
14
+ export * from './controls/section';
15
+ export * from './controls/svg';
16
+ export * from './controls/vertical';
17
+ export * from './controls/vertical-content';
18
+ export * from './helpers/hooks';
19
+ export { KnownClassNames } from './styles/styles';
@@ -0,0 +1,87 @@
1
+ import { GriffelStyle, makeStyles, tokens } from "@fluentui/react-components";
2
+
3
+ export module mixins {
4
+ export const main: GriffelStyle = {
5
+ flexShrink: 1,
6
+ flexGrow: 1
7
+ };
8
+ export const clickable: GriffelStyle = {
9
+ cursor: "pointer",
10
+ ['& label']: {
11
+ cursor: "pointer"
12
+ }
13
+ }
14
+ const box: GriffelStyle = {
15
+ padding: tokens.spacingHorizontalM,
16
+ borderRadius: tokens.borderRadiusMedium,
17
+ boxShadow: tokens.shadow4,
18
+ margin: tokens.spacingHorizontalXXS
19
+ }
20
+ export const float: GriffelStyle = {
21
+ ...box,
22
+ /** make buttons work */
23
+ position: "relative",
24
+ /** make buttons work */
25
+ maxWidth: "33%",
26
+ /** stop bleeding into page */
27
+ overflowX: "hidden",
28
+
29
+ ['& img']: {
30
+ maxWidth: "100%"
31
+ }
32
+ }
33
+
34
+ export const flex: GriffelStyle = {
35
+ display: 'flex',
36
+ flexWrap: 'nowrap',
37
+ rowGap: tokens.spacingVerticalM,
38
+ columnGap: tokens.spacingVerticalM,
39
+ }
40
+
41
+ export const wrap: GriffelStyle = {
42
+ flexWrap: "wrap"
43
+ }
44
+ export const nogap: GriffelStyle = {
45
+ rowGap: 0,
46
+ columnGap: 0
47
+ }
48
+ export const ellipsis: GriffelStyle = {
49
+ whiteSpace: 'nowrap',
50
+ display: 'block',
51
+ overflow: 'hidden',
52
+ textOverflow: 'ellipsis'
53
+ }
54
+
55
+ // export const box: GriffelStyle = {
56
+ // }
57
+ }
58
+
59
+ export const KnownClassNames = {
60
+ print: 'print-root',
61
+ section: 'kfui-section',
62
+ vertical: 'kfui-vertical',
63
+ horizontal: 'kfui-horizontal',
64
+ list: 'kfui-list'
65
+ }
66
+ export const useCommonStyles = makeStyles({
67
+ printShow: {
68
+ display: 'none',
69
+ [`:global(body.${KnownClassNames.print})`]: {
70
+ display: 'unset',
71
+ },
72
+ '@media print': {
73
+ display: 'unset',
74
+ }
75
+ },
76
+ printHide: {
77
+ [`:global(body.${KnownClassNames.print})`]: {
78
+ display: 'none !important'
79
+ },
80
+ '@media print': {
81
+ display: 'none !important'
82
+ }
83
+ },
84
+ })
85
+
86
+ export const widthMedium = 360;
87
+ //export const widthWide = 520;