@kwiz/fluentui 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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;