@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.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/_modules/build.d.ts +2 -0
- package/dist/_modules/build.js +3 -0
- package/dist/_modules/build.js.map +1 -0
- package/dist/_modules/config.d.ts +1 -0
- package/dist/_modules/config.js +9 -0
- package/dist/_modules/config.js.map +1 -0
- package/dist/_modules/constants.d.ts +2 -0
- package/dist/_modules/constants.js +3 -0
- package/dist/_modules/constants.js.map +1 -0
- package/dist/_modules/exports-index.d.ts +1 -0
- package/dist/_modules/exports-index.js +2 -0
- package/dist/_modules/exports-index.js.map +1 -0
- package/dist/controls/button.d.ts +27 -0
- package/dist/controls/button.js +100 -0
- package/dist/controls/button.js.map +1 -0
- package/dist/controls/centered.d.ts +5 -0
- package/dist/controls/centered.js +14 -0
- package/dist/controls/centered.js.map +1 -0
- package/dist/controls/dropdown.d.ts +18 -0
- package/dist/controls/dropdown.js +13 -0
- package/dist/controls/dropdown.js.map +1 -0
- package/dist/controls/error-boundary.d.ts +23 -0
- package/dist/controls/error-boundary.js +33 -0
- package/dist/controls/error-boundary.js.map +1 -0
- package/dist/controls/exports-index.d.ts +17 -0
- package/dist/controls/exports-index.js +18 -0
- package/dist/controls/exports-index.js.map +1 -0
- package/dist/controls/field-editor.d.ts +13 -0
- package/dist/controls/field-editor.js +15 -0
- package/dist/controls/field-editor.js.map +1 -0
- package/dist/controls/file-upload.d.ts +18 -0
- package/dist/controls/file-upload.js +41 -0
- package/dist/controls/file-upload.js.map +1 -0
- package/dist/controls/horizontal.d.ts +8 -0
- package/dist/controls/horizontal.js +23 -0
- package/dist/controls/horizontal.js.map +1 -0
- package/dist/controls/input.d.ts +13 -0
- package/dist/controls/input.js +41 -0
- package/dist/controls/input.js.map +1 -0
- package/dist/controls/list.d.ts +21 -0
- package/dist/controls/list.js +72 -0
- package/dist/controls/list.js.map +1 -0
- package/dist/controls/loading.d.ts +5 -0
- package/dist/controls/loading.js +7 -0
- package/dist/controls/loading.js.map +1 -0
- package/dist/controls/please-wait.d.ts +17 -0
- package/dist/controls/please-wait.js +16 -0
- package/dist/controls/please-wait.js.map +1 -0
- package/dist/controls/prompt.d.ts +16 -0
- package/dist/controls/prompt.js +21 -0
- package/dist/controls/prompt.js.map +1 -0
- package/dist/controls/search.d.ts +13 -0
- package/dist/controls/search.js +47 -0
- package/dist/controls/search.js.map +1 -0
- package/dist/controls/section.d.ts +14 -0
- package/dist/controls/section.js +27 -0
- package/dist/controls/section.js.map +1 -0
- package/dist/controls/svg.d.ts +23 -0
- package/dist/controls/svg.js +45 -0
- package/dist/controls/svg.js.map +1 -0
- package/dist/controls/vertical-content.d.ts +6 -0
- package/dist/controls/vertical-content.js +37 -0
- package/dist/controls/vertical-content.js.map +1 -0
- package/dist/controls/vertical.d.ts +8 -0
- package/dist/controls/vertical.js +23 -0
- package/dist/controls/vertical.js.map +1 -0
- package/dist/exports-index.d.ts +3 -0
- package/dist/exports-index.js +4 -0
- package/dist/exports-index.js.map +1 -0
- package/dist/helpers/hooks.d.ts +22 -0
- package/dist/helpers/hooks.js +173 -0
- package/dist/helpers/hooks.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/exports-index.d.ts +2 -0
- package/dist/styles/exports-index.js +3 -0
- package/dist/styles/exports-index.js.map +1 -0
- package/dist/styles/styles.d.ts +19 -0
- package/dist/styles/styles.js +79 -0
- package/dist/styles/styles.js.map +1 -0
- package/dist/styles/theme.d.ts +6 -0
- package/dist/styles/theme.js +77 -0
- package/dist/styles/theme.js.map +1 -0
- package/package.json +71 -0
- package/src/_modules/config.ts +9 -0
- package/src/_modules/constants.ts +3 -0
- package/src/controls/button.tsx +154 -0
- package/src/controls/centered.tsx +23 -0
- package/src/controls/dropdown.tsx +28 -0
- package/src/controls/error-boundary.tsx +42 -0
- package/src/controls/field-editor.tsx +42 -0
- package/src/controls/file-upload.tsx +68 -0
- package/src/controls/horizontal.tsx +35 -0
- package/src/controls/input.tsx +59 -0
- package/src/controls/list.tsx +118 -0
- package/src/controls/loading.tsx +11 -0
- package/src/controls/please-wait.tsx +32 -0
- package/src/controls/prompt.tsx +59 -0
- package/src/controls/search.tsx +66 -0
- package/src/controls/section.tsx +52 -0
- package/src/controls/svg.tsx +120 -0
- package/src/controls/vertical-content.tsx +50 -0
- package/src/controls/vertical.tsx +35 -0
- package/src/helpers/hooks.ts +189 -0
- package/src/index.ts +19 -0
- package/src/styles/styles.ts +87 -0
- 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;
|