@kwiz/fluentui 1.0.60 → 1.0.62
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/README.md +37 -11
- package/dist/controls/button.d.ts +11 -11
- package/dist/controls/button.js +2 -3
- package/dist/controls/button.js.map +1 -1
- package/dist/controls/centered.js +1 -8
- package/dist/controls/centered.js.map +1 -1
- package/dist/controls/date.js +1 -1
- package/dist/controls/date.js.map +1 -1
- package/dist/controls/diagram-picker.d.ts +2 -2
- package/dist/controls/divider.d.ts +2 -2
- package/dist/controls/dropdown.d.ts +4 -9
- package/dist/controls/dropdown.js +27 -28
- package/dist/controls/dropdown.js.map +1 -1
- package/dist/controls/file-upload.d.ts +1 -1
- package/dist/controls/horizontal.d.ts +2 -2
- package/dist/controls/index.d.ts +1 -0
- package/dist/controls/index.js +1 -0
- package/dist/controls/index.js.map +1 -1
- package/dist/controls/input.js +1 -1
- package/dist/controls/input.js.map +1 -1
- package/dist/controls/list.js +4 -1
- package/dist/controls/list.js.map +1 -1
- package/dist/controls/merge-text.d.ts +16 -0
- package/dist/controls/merge-text.js +81 -0
- package/dist/controls/merge-text.js.map +1 -0
- package/dist/controls/progress-bar.d.ts +2 -2
- package/dist/controls/prompt.d.ts +1 -1
- package/dist/controls/search.js +2 -8
- package/dist/controls/search.js.map +1 -1
- package/dist/controls/section.d.ts +3 -2
- package/dist/controls/section.js +15 -5
- package/dist/controls/section.js.map +1 -1
- package/dist/controls/svg.d.ts +3 -0
- package/dist/controls/svg.js +5 -0
- package/dist/controls/svg.js.map +1 -1
- package/dist/controls/toolbar.js +2 -3
- package/dist/controls/toolbar.js.map +1 -1
- package/dist/controls/vertical.d.ts +4 -2
- package/dist/controls/vertical.js +6 -1
- package/dist/controls/vertical.js.map +1 -1
- package/dist/helpers/context-const.d.ts +2 -0
- package/dist/helpers/context-const.js.map +1 -1
- package/dist/helpers/context-export.d.ts +9 -1
- package/dist/helpers/context-export.js +43 -6
- package/dist/helpers/context-export.js.map +1 -1
- package/dist/helpers/forwardRef.d.ts +4 -0
- package/dist/helpers/forwardRef.js +2 -0
- package/dist/helpers/forwardRef.js.map +1 -0
- package/dist/helpers/hooks-events.d.ts +3 -3
- package/dist/helpers/hooks-events.js.map +1 -1
- package/dist/helpers/hooks.d.ts +1 -1
- package/dist/helpers/hooks.js +15 -6
- package/dist/helpers/hooks.js.map +1 -1
- package/dist/styles/styles.d.ts +5 -1
- package/dist/styles/styles.js +5 -18
- package/dist/styles/styles.js.map +1 -1
- package/package.json +3 -1
- package/src/controls/button.tsx +2 -3
- package/src/controls/centered.tsx +2 -10
- package/src/controls/date.tsx +1 -1
- package/src/controls/dropdown.tsx +37 -36
- package/src/controls/index.ts +2 -0
- package/src/controls/input.tsx +1 -1
- package/src/controls/list.tsx +4 -2
- package/src/controls/merge-text.tsx +126 -0
- package/src/controls/search.tsx +0 -4
- package/src/controls/section.tsx +17 -5
- package/src/controls/svg.tsx +8 -0
- package/src/controls/toolbar.tsx +2 -4
- package/src/controls/vertical.tsx +9 -1
- package/src/helpers/context-const.ts +2 -0
- package/src/helpers/context-export.tsx +51 -7
- package/src/helpers/forwardRef.ts +7 -0
- package/src/helpers/hooks-events.ts +2 -2
- package/src/helpers/hooks.tsx +16 -7
- package/src/styles/styles.ts +5 -18
@@ -1,18 +1,20 @@
|
|
1
1
|
import { Dropdown, DropdownProps, makeStyles, mergeClasses, Option } from '@fluentui/react-components';
|
2
2
|
import { filterEmptyEntries, firstOrNull, isNullOrUndefined } from '@kwiz/common';
|
3
3
|
import React from 'react';
|
4
|
+
import { GetLogger } from '../_modules/config';
|
4
5
|
import { useKWIZFluentContext } from '../helpers/context-internal';
|
5
6
|
|
7
|
+
const logger = GetLogger("DropdownEX");
|
8
|
+
|
6
9
|
const useStyles = makeStyles({
|
7
10
|
root: {
|
8
11
|
minWidth: "auto"
|
9
12
|
},
|
10
13
|
});
|
11
14
|
|
12
|
-
|
13
15
|
type ForwardProps = Omit<DropdownProps, "onSelect" | "selectedOptions" | "clearable">;
|
14
16
|
|
15
|
-
interface IProps<
|
17
|
+
interface IProps<keyType, dataType> extends ForwardProps {
|
16
18
|
required?: boolean;
|
17
19
|
selected: keyType | keyType[];
|
18
20
|
items: {
|
@@ -27,40 +29,39 @@ interface IProps<dataType, keyType extends string = string> extends ForwardProps
|
|
27
29
|
options?: { key: keyType, value: string, data?: dataType }[]) => void;
|
28
30
|
}
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
*/
|
35
|
-
export function getDropdownEX<keyType extends string = string, dataType = never>() {
|
36
|
-
return React.forwardRef<HTMLButtonElement, (IProps<dataType, keyType>)>((props, ref) => {
|
37
|
-
const classes = useStyles();
|
38
|
-
const ctx = useKWIZFluentContext();
|
39
|
-
const selected: keyType[] = Array.isArray(props.selected) ? props.selected : isNullOrUndefined(props.selected) ? [] : [props.selected];
|
32
|
+
function $DropdownEX<keyType extends string = string, dataType = never>(props: IProps<keyType, dataType>, ref: React.ForwardedRef<HTMLButtonElement>) {
|
33
|
+
const classes = useStyles();
|
34
|
+
const ctx = useKWIZFluentContext();
|
35
|
+
const selected: keyType[] = Array.isArray(props.selected) ? props.selected : isNullOrUndefined(props.selected) ? [] : [props.selected];
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
37
|
+
//sometimes control will lose value when re-rendered
|
38
|
+
//use case: public forms when editing other fields after the dropdown was set
|
39
|
+
//re-set the text value manually to fix
|
40
|
+
let text = filterEmptyEntries((Array.isArray(props.selected) ? props.selected : [props.selected]).map(s => {
|
41
|
+
let v = firstOrNull(props.items, i => i.key === s);
|
42
|
+
return v ? v.value : ''
|
43
|
+
})).join(', ');
|
48
44
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
});
|
45
|
+
return (
|
46
|
+
<Dropdown {...{ ...props, onSelect: undefined }} className={mergeClasses(classes.root, props.className)} ref={ref} clearable={!props.required && !props.multiselect}
|
47
|
+
appearance={ctx.inputAppearance} mountNode={ctx.mountNode}
|
48
|
+
selectedOptions={selected} value={text} onOptionSelect={(e, data) => {
|
49
|
+
let o = firstOrNull(props.items, i => i.key === data.optionValue);
|
50
|
+
if (props.multiselect) {
|
51
|
+
let current = data.selectedOptions.map(s => firstOrNull(props.items, i => i.key === s));
|
52
|
+
props.onSelect(o, current);
|
53
|
+
}
|
54
|
+
else props.onSelect(o);
|
55
|
+
}}>
|
56
|
+
{props.items.map(i => <Option key={i.key} value={i.key} text={i.value}>{i.option ? i.option : i.value}</Option>)}
|
57
|
+
</Dropdown>
|
58
|
+
);
|
64
59
|
}
|
65
|
-
|
66
|
-
export const DropdownEX =
|
60
|
+
|
61
|
+
export const DropdownEX = React.forwardRef($DropdownEX);
|
62
|
+
|
63
|
+
/** @deprecated use normal DropdownEX it is now generic */
|
64
|
+
export function getDropdownEX<keyType extends string = string, dataType = never>() {
|
65
|
+
logger.warn('getDropdownEX is deprecated. use DropdownEX it now supports generic types');
|
66
|
+
return React.forwardRef($DropdownEX<keyType, dataType>);
|
67
|
+
}
|
package/src/controls/index.ts
CHANGED
@@ -19,6 +19,7 @@ export * from './kwizoverflow';
|
|
19
19
|
export * from './list';
|
20
20
|
export * from './loading';
|
21
21
|
export * from './menu';
|
22
|
+
export * from './merge-text';
|
22
23
|
export * from './please-wait';
|
23
24
|
export * from './progress-bar';
|
24
25
|
export * from './prompt';
|
@@ -29,3 +30,4 @@ export * from './svg';
|
|
29
30
|
export * from './toolbar';
|
30
31
|
export * from './vertical';
|
31
32
|
export * from './vertical-content';
|
33
|
+
|
package/src/controls/input.tsx
CHANGED
@@ -112,7 +112,7 @@ export const InputNumberEx: React.FunctionComponent<React.PropsWithChildren<INum
|
|
112
112
|
const isValid = props.required ? !isNullOrNaN(asNumber) : isNullOrUndefined(asNumber) || !isNaN(asNumber);
|
113
113
|
setIsValid(isValid);
|
114
114
|
props.onChange(isValid ? asNumber : null);
|
115
|
-
}, [props.allowDecimals]);
|
115
|
+
}, [props.allowDecimals, props.onChange, props.required]);
|
116
116
|
|
117
117
|
const passProps: IProps = { ...props, defaultValue: undefined, value: undefined, onChange: undefined };
|
118
118
|
|
package/src/controls/list.tsx
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { makeStyles, tokens } from '@fluentui/react-components';
|
2
2
|
import { LOGO_BLUE_SQUARE, LOGO_WHITE_SQUARE, isNullOrUndefined, isString } from '@kwiz/common';
|
3
3
|
import React from 'react';
|
4
|
+
import { useKWIZFluentContext } from '../helpers/context-internal';
|
4
5
|
import { KnownClassNames, mixins } from '../styles/styles';
|
5
6
|
import { Horizontal } from './horizontal';
|
6
7
|
import { Section } from './section';
|
@@ -85,8 +86,9 @@ interface IProps {
|
|
85
86
|
}
|
86
87
|
|
87
88
|
export const ListEx = (props: IProps) => {
|
88
|
-
|
89
|
+
const ctx = useKWIZFluentContext();
|
89
90
|
const cssNames = useStyles();
|
91
|
+
const isDark = ctx.dark === true || props.dark === true;
|
90
92
|
|
91
93
|
const listItemElm = (item: iListItem) => <Horizontal key={item.key} css={[cssNames.listItem, item.selected && cssNames.listItemSelected]} onClick={item.onClick}>
|
92
94
|
{item.media && <Section css={[cssNames.media]} onClick={(e) => {
|
@@ -94,7 +96,7 @@ export const ListEx = (props: IProps) => {
|
|
94
96
|
e.stopPropagation();//media may have its on onclick
|
95
97
|
}}>{
|
96
98
|
isString(item.media)
|
97
|
-
? <div className={cssNames.image} style={{ backgroundImage: `url('${encodeURI(item.media)}'), url('${
|
99
|
+
? <div className={cssNames.image} style={{ backgroundImage: `url('${encodeURI(item.media)}'), url('${isDark ? LOGO_WHITE_SQUARE : LOGO_BLUE_SQUARE}')` }}></div>
|
98
100
|
: item.media
|
99
101
|
}</Section>}
|
100
102
|
<Vertical main css={[cssNames.listItemBody]}>
|
@@ -0,0 +1,126 @@
|
|
1
|
+
import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, Field, Label, makeStyles, Radio, tokens } from '@fluentui/react-components';
|
2
|
+
import { DismissRegular, SaveRegular } from '@fluentui/react-icons';
|
3
|
+
import { isNullOrUndefined, waitFor } from '@kwiz/common';
|
4
|
+
import { DefaultDarkColors, DefaultLightColors, MisMerge2 } from '@mismerge/react';
|
5
|
+
import * as React from 'react';
|
6
|
+
|
7
|
+
import '@mismerge/core/dark.css';
|
8
|
+
import '@mismerge/core/styles.css';
|
9
|
+
import { useStateEX, useWindowSize } from '../helpers';
|
10
|
+
import { useKWIZFluentContext } from '../helpers/context-internal';
|
11
|
+
import { ButtonEX, ButtonEXPrimarySubtle } from './button';
|
12
|
+
import { Horizontal } from './horizontal';
|
13
|
+
import { Section } from './section';
|
14
|
+
import { Vertical } from './vertical';
|
15
|
+
|
16
|
+
const useStyles = makeStyles({
|
17
|
+
root: {
|
18
|
+
// position: 'fixed',
|
19
|
+
// top: 0,
|
20
|
+
// left: 0,
|
21
|
+
// right: 0,
|
22
|
+
// bottom: 0,
|
23
|
+
// backgroundColor: tokens.colorNeutralBackground1,
|
24
|
+
// zIndex: 10,
|
25
|
+
"& .mismerge": {
|
26
|
+
// height: "100%",
|
27
|
+
"--background": tokens.colorNeutralBackground1,
|
28
|
+
//line number background
|
29
|
+
"--primary-100": tokens.colorNeutralBackground2,
|
30
|
+
//selection background
|
31
|
+
"--selection": tokens.colorNeutralBackground1Selected,
|
32
|
+
//scrollbar hover
|
33
|
+
"--primary-300": tokens.colorNeutralBackground1Hover,
|
34
|
+
//border / scroll
|
35
|
+
"--primary-200": tokens.colorNeutralStroke1,
|
36
|
+
//line number color
|
37
|
+
"--primary-400": tokens.colorNeutralForeground2,
|
38
|
+
//button hover color
|
39
|
+
"--primary-500": tokens.colorNeutralForeground1Hover,
|
40
|
+
//main text color
|
41
|
+
"--primary-600": tokens.colorNeutralForeground1,
|
42
|
+
"& TEXTAREA": {
|
43
|
+
lineHeight: "20px"
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
menu: {
|
48
|
+
justifyContent: "space-between"
|
49
|
+
}
|
50
|
+
});
|
51
|
+
|
52
|
+
interface IProps {
|
53
|
+
title: string;
|
54
|
+
description?: string;
|
55
|
+
lhsTitle: string;
|
56
|
+
lhsValue: string;
|
57
|
+
rhsTitle: string;
|
58
|
+
rhsValue: string;
|
59
|
+
dark?: boolean;
|
60
|
+
save: (merged: string) => void;
|
61
|
+
cancel: () => void;
|
62
|
+
}
|
63
|
+
export const MergeText: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
|
64
|
+
const classes = useStyles();
|
65
|
+
const ctx = useKWIZFluentContext();
|
66
|
+
|
67
|
+
let size = useWindowSize();
|
68
|
+
let wrapper = React.useRef<HTMLDivElement>();
|
69
|
+
let [lhs, setLhs] = useStateEX(props.lhsValue || "", {
|
70
|
+
skipUpdateIfSame: true, onChange: (v, changed) => {
|
71
|
+
if (changed) setKeep("left"); return v;
|
72
|
+
}
|
73
|
+
});
|
74
|
+
let [rhs, setRhs] = useStateEX(props.rhsValue || "", {
|
75
|
+
skipUpdateIfSame: true, onChange: (v, changed) => {
|
76
|
+
if (changed) setKeep("right"); return v;
|
77
|
+
}
|
78
|
+
});
|
79
|
+
let [keep, setKeep] = useStateEX<"cancel" | "left" | "right">("cancel");
|
80
|
+
|
81
|
+
React.useEffect(() => {
|
82
|
+
if (wrapper.current) {
|
83
|
+
waitFor(() => !isNullOrUndefined(wrapper.current.querySelector(".mismerge"))).then(() => {
|
84
|
+
let mismerge = wrapper.current.querySelector(".mismerge") as HTMLDivElement;
|
85
|
+
if (mismerge)
|
86
|
+
mismerge.style.height = `${mismerge.offsetParent.clientHeight - mismerge.offsetTop - 10}px`;
|
87
|
+
});
|
88
|
+
}
|
89
|
+
}, [wrapper.current, size.height]);
|
90
|
+
|
91
|
+
return <Drawer type='overlay' open size='full' className={classes.root} mountNode={ctx.mountNode}>
|
92
|
+
<DrawerHeader>
|
93
|
+
<DrawerHeaderTitle action={<ButtonEX icon={<DismissRegular />} title="Close" onClick={props.cancel} />}>
|
94
|
+
{props.title}
|
95
|
+
</DrawerHeaderTitle>
|
96
|
+
</DrawerHeader>
|
97
|
+
<DrawerBody>
|
98
|
+
<Vertical>
|
99
|
+
{props.description && <Label>{props.description}</Label>}
|
100
|
+
<Field label="Which version would you like to keep?"
|
101
|
+
hint="Merge the changes to either side and save. Close this panel to keep editing the page without saving">
|
102
|
+
<Horizontal css={[classes.menu]}>
|
103
|
+
<Horizontal nogap>
|
104
|
+
<Radio value="left" label={props.lhsTitle} checked={keep === "left"} onClick={() => setKeep("left")} />
|
105
|
+
<ButtonEXPrimarySubtle showTitleWithIcon dontCenterText icon={<SaveRegular />} disabled={keep !== "left"} title={`Save ${props.lhsTitle.toLowerCase()}`} onClick={() => props.save(lhs)} />
|
106
|
+
</Horizontal>
|
107
|
+
<Horizontal nogap>
|
108
|
+
<Radio value="right" label={props.rhsTitle} checked={keep === "right"} onClick={() => setKeep("right")} />
|
109
|
+
<ButtonEXPrimarySubtle showTitleWithIcon dontCenterText icon={<SaveRegular />} disabled={keep !== "right"} title={`Save ${props.rhsTitle.toLowerCase()}`} onClick={() => props.save(rhs)} />
|
110
|
+
</Horizontal>
|
111
|
+
</Horizontal>
|
112
|
+
</Field>
|
113
|
+
<Section main ref={wrapper}>
|
114
|
+
<MisMerge2
|
115
|
+
lhs={lhs}
|
116
|
+
rhs={rhs}
|
117
|
+
lhsEditable rhsEditable
|
118
|
+
onLhsChange={v => setLhs(v)}
|
119
|
+
onRhsChange={v => setRhs(v)}
|
120
|
+
colors={props.dark ? DefaultDarkColors : DefaultLightColors}
|
121
|
+
/>
|
122
|
+
</Section>
|
123
|
+
</Vertical>
|
124
|
+
</DrawerBody>
|
125
|
+
</Drawer>;
|
126
|
+
}
|
package/src/controls/search.tsx
CHANGED
package/src/controls/section.tsx
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { makeStyles, mergeClasses, Portal, tokens } from '@fluentui/react-components';
|
2
|
-
import { isFunction, isNotEmptyArray } from '@kwiz/common';
|
2
|
+
import { isFunction, isNotEmptyArray, isNotEmptyString } from '@kwiz/common';
|
3
3
|
import React from 'react';
|
4
4
|
import { useKWIZFluentContext } from '../helpers/context-internal';
|
5
5
|
import { KnownClassNames, mixins, useCommonStyles } from '../styles/styles';
|
@@ -15,7 +15,10 @@ const useStyles = makeStyles({
|
|
15
15
|
right: {
|
16
16
|
...mixins.float,
|
17
17
|
float: "right",
|
18
|
-
|
18
|
+
marginLeft: tokens.spacingHorizontalXXL
|
19
|
+
},
|
20
|
+
selfCentered: {
|
21
|
+
alignSelf: "center"
|
19
22
|
}
|
20
23
|
});
|
21
24
|
|
@@ -31,6 +34,7 @@ export interface ISectionProps {
|
|
31
34
|
right?: boolean;
|
32
35
|
/** true - will add css position fixed. portal will also wrap it in a portal. */
|
33
36
|
fullscreen?: boolean | "portal";
|
37
|
+
centerSelf?: boolean;
|
34
38
|
}
|
35
39
|
|
36
40
|
export const Section = React.forwardRef<HTMLDivElement, React.PropsWithChildren<ISectionProps>>((props, ref) => {
|
@@ -39,13 +43,21 @@ export const Section = React.forwardRef<HTMLDivElement, React.PropsWithChildren<
|
|
39
43
|
const cssNames = useStyles();
|
40
44
|
let css: string[] = [KnownClassNames.section];
|
41
45
|
if (props.main) css.push(cssNames.main);
|
46
|
+
if (props.centerSelf) css.push(cssNames.selfCentered);
|
42
47
|
if (isFunction(props.onClick))
|
43
48
|
css.push(cssNames.clickable);
|
44
49
|
|
45
|
-
if (props.left)
|
46
|
-
|
50
|
+
if (props.left) {
|
51
|
+
css.push(cssNames.left);
|
52
|
+
css.push(KnownClassNames.left);
|
53
|
+
}
|
54
|
+
else if (props.right) {
|
55
|
+
css.push(cssNames.right);
|
56
|
+
css.push(KnownClassNames.right);
|
57
|
+
}
|
47
58
|
|
48
|
-
|
59
|
+
//a css class might have space and multiuple classes in it
|
60
|
+
if (isNotEmptyArray(props.css)) props.css.filter(c => isNotEmptyString(c)).forEach(c => css.push(...c.split(" ")));
|
49
61
|
if (props.fullscreen) css.push(commonStyles.fullscreen);
|
50
62
|
const control = <div ref={ref} {...(props.rootProps || {})} title={props.title} style={props.style}
|
51
63
|
className={mergeClasses(...css)}
|
package/src/controls/svg.tsx
CHANGED
@@ -121,6 +121,14 @@ export const GetSVGSplitIcon = (props: { size: number }) => {
|
|
121
121
|
</svg>`;
|
122
122
|
}
|
123
123
|
|
124
|
+
export function GetSVGCopyIcon(props: { size: number }) {
|
125
|
+
return (
|
126
|
+
`<svg fill="var(--colorNeutralForeground1)" width="${props.size}px" height="${props.size}px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
127
|
+
<path d="M8 2a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8ZM7 4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4ZM4 6a2 2 0 0 1 1-1.73V14.5A2.5 2.5 0 0 0 7.5 17h6.23A2 2 0 0 1 12 18H7.5A3.5 3.5 0 0 1 4 14.5V6Z"></path>
|
128
|
+
</svg>`
|
129
|
+
)
|
130
|
+
}
|
131
|
+
|
124
132
|
export function IconToSVG(icon: JSX.Element) {
|
125
133
|
const iconDiv = document.createElement('div');
|
126
134
|
const root = createRoot(iconDiv);
|
package/src/controls/toolbar.tsx
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Toolbar, ToolbarDivider, ToolbarGroup } from '@fluentui/react-components';
|
2
2
|
import React from 'react';
|
3
|
-
import {
|
3
|
+
import { KnownClassNames } from '../styles/styles';
|
4
4
|
import { KWIZOverflow } from './kwizoverflow';
|
5
5
|
|
6
6
|
interface IProps {
|
@@ -10,8 +10,6 @@ interface IProps {
|
|
10
10
|
sideButtons?: JSX.Element;
|
11
11
|
}
|
12
12
|
export const ToolbarEX: React.FunctionComponent<IProps> = (props) => {
|
13
|
-
const commonCssNames = useCommonStyles();
|
14
|
-
|
15
13
|
let elements: { id: string, priority?: number, elm: JSX.Element, overflowElement?: JSX.Element }[] = [];
|
16
14
|
props.buttonGroups.forEach((group, groupIndex) => {
|
17
15
|
group.forEach((button, buttonIndex) => {
|
@@ -32,7 +30,7 @@ export const ToolbarEX: React.FunctionComponent<IProps> = (props) => {
|
|
32
30
|
});
|
33
31
|
|
34
32
|
return (
|
35
|
-
<KWIZOverflow className={
|
33
|
+
<KWIZOverflow className={KnownClassNames.printHide}
|
36
34
|
items={elements}
|
37
35
|
getKey={e => e.id}
|
38
36
|
renderItem={(e, i, overflow) => overflow && e.overflowElement || e.elm}
|
@@ -10,12 +10,18 @@ const useStyles = makeStyles({
|
|
10
10
|
flexDirection: 'column'
|
11
11
|
},
|
12
12
|
wrap: mixins.wrap,
|
13
|
-
nogap: mixins.nogap
|
13
|
+
nogap: mixins.nogap,
|
14
|
+
vCentered: {
|
15
|
+
justifyContent: "center"
|
16
|
+
},
|
17
|
+
|
14
18
|
})
|
15
19
|
|
16
20
|
interface IProps extends ISectionProps {
|
17
21
|
wrap?: boolean;
|
18
22
|
nogap?: boolean;
|
23
|
+
/** vertical centered */
|
24
|
+
vCentered?: boolean;
|
19
25
|
}
|
20
26
|
export const Vertical = React.forwardRef<HTMLDivElement, React.PropsWithChildren<IProps>>((props, ref) => {
|
21
27
|
const cssNames = useStyles();
|
@@ -26,6 +32,8 @@ export const Vertical = React.forwardRef<HTMLDivElement, React.PropsWithChildren
|
|
26
32
|
css.push(cssNames.wrap);
|
27
33
|
if (props.nogap)
|
28
34
|
css.push(cssNames.nogap);
|
35
|
+
if (props.vCentered)
|
36
|
+
css.push(cssNames.vCentered);
|
29
37
|
|
30
38
|
if (isNotEmptyArray(props.css)) css.push(...props.css);
|
31
39
|
|
@@ -1,31 +1,75 @@
|
|
1
|
-
import { makeStyles } from "@fluentui/react-components";
|
1
|
+
import { makeStaticStyles, makeStyles } from "@fluentui/react-components";
|
2
2
|
import React, { PropsWithChildren, useEffect, useState } from "react";
|
3
|
+
import { GetLogger } from "../_modules/config";
|
4
|
+
import { KnownClassNames } from "../styles";
|
3
5
|
import { iKWIZFluentContext, KWIZFluentContext } from "./context-const";
|
6
|
+
import { DragDropContextProvider } from "./drag-drop";
|
4
7
|
export type { iKWIZFluentContext } from "./context-const";
|
5
|
-
|
8
|
+
const logger = GetLogger("KWIZFluentContextProvider");
|
6
9
|
const useContextStyles = makeStyles({
|
7
10
|
root: {
|
8
11
|
"& *": {
|
9
12
|
scrollbarWidth: "thin"
|
10
|
-
}
|
13
|
+
},
|
14
|
+
[`& .${KnownClassNames.printShow}`]: {
|
15
|
+
'@media print': {
|
16
|
+
display: 'unset',
|
17
|
+
}
|
18
|
+
},
|
19
|
+
[`& .${KnownClassNames.printHide}`]: {
|
20
|
+
'@media print': {
|
21
|
+
display: 'none !important'
|
22
|
+
}
|
23
|
+
},
|
24
|
+
},
|
25
|
+
});
|
26
|
+
export const useStaticStyles = makeStaticStyles({
|
27
|
+
[`.${KnownClassNames.printShow}`]: {
|
28
|
+
display: 'none'
|
11
29
|
},
|
12
|
-
}
|
30
|
+
[`body.${KnownClassNames.print} .${KnownClassNames.printHide}`]: {
|
31
|
+
display: "none !important"
|
32
|
+
},
|
33
|
+
[`body.${KnownClassNames.print} .${KnownClassNames.printShow}`]: {
|
34
|
+
display: "unset"
|
35
|
+
}
|
36
|
+
});
|
13
37
|
|
38
|
+
/** @deprecated - use KWIZFluentProvider instead of using this + DragDropContextProvider */
|
14
39
|
export function useKWIZFluentContextProvider(options: {
|
15
40
|
root?: React.MutableRefObject<HTMLDivElement>;
|
16
41
|
ctx?: iKWIZFluentContext;
|
17
42
|
}) {
|
43
|
+
useStaticStyles();
|
18
44
|
const classes = useContextStyles();
|
19
45
|
let v: iKWIZFluentContext = options && options.ctx || {};
|
20
46
|
const [kwizFluentContext, setKwizFluentContext] = useState<iKWIZFluentContext>(v);
|
21
47
|
useEffect(() => {
|
22
|
-
options.root?.current
|
48
|
+
if (options.root?.current) logger.warn('Sending a root node is not recommended, if you have set up your packages correctly to mark react and fluent UI as external dialogs should open correctly.');
|
49
|
+
let styleRoot = options.root?.current || document.body;
|
50
|
+
styleRoot.classList.add(...classes.root.split(' '));
|
23
51
|
// ref only updates in useEffect, not in useMemo or anything else.
|
24
52
|
// we need to set it into state so it will trigger a ui update
|
25
53
|
setKwizFluentContext({
|
26
54
|
...v,
|
27
|
-
mountNode: options.root
|
55
|
+
mountNode: options.root?.current
|
28
56
|
});
|
29
57
|
}, [options.root]);
|
30
|
-
return
|
58
|
+
return {
|
59
|
+
KWIZFluentContext,
|
60
|
+
value: kwizFluentContext
|
61
|
+
};
|
62
|
+
}
|
63
|
+
|
64
|
+
export const KWIZFluentProvider = (props: PropsWithChildren<{
|
65
|
+
ctx?: iKWIZFluentContext;
|
66
|
+
}>) => {
|
67
|
+
|
68
|
+
const cp = useKWIZFluentContextProvider({ ctx: props.ctx });
|
69
|
+
|
70
|
+
return <cp.KWIZFluentContext.Provider value={cp.value}>
|
71
|
+
<DragDropContextProvider>
|
72
|
+
{props.children}
|
73
|
+
</DragDropContextProvider>
|
74
|
+
</cp.KWIZFluentContext.Provider>;
|
31
75
|
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
//need to redeclare to support forward ref with generic types
|
3
|
+
declare module "react" {
|
4
|
+
function forwardRef<T, P = {}>(
|
5
|
+
render: (props: P, ref: React.Ref<T>) => React.JSX.Element | null
|
6
|
+
): (props: P & React.RefAttributes<T>) => React.JSX.Element | null;
|
7
|
+
}
|
@@ -3,8 +3,8 @@ import { MutableRefObject, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { KnownClassNames } from "../styles/styles";
|
4
4
|
import { useEffectOnlyOnMount } from "./hooks";
|
5
5
|
|
6
|
-
export function useTrackFocus(props: { onFocus: () => void, onLoseFocus: () => void, ref?: MutableRefObject<
|
7
|
-
const wrapperDiv = props.ref || useRef<HTMLDivElement>(null);
|
6
|
+
export function useTrackFocus<elmType extends HTMLElement>(props: { onFocus: () => void, onLoseFocus: () => void, ref?: MutableRefObject<elmType> }) {
|
7
|
+
const wrapperDiv: MutableRefObject<elmType> = props.ref || useRef<HTMLDivElement>(null) as any;
|
8
8
|
useEffect(() => {
|
9
9
|
function focusIn(e: FocusEvent) {
|
10
10
|
let elm = e.target as HTMLElement;//document.activeElement;
|
package/src/helpers/hooks.tsx
CHANGED
@@ -2,13 +2,12 @@ import { isFunction, isNotEmptyArray, isNullOrEmptyString, isPrimitiveValue, jso
|
|
2
2
|
import { MutableRefObject, SetStateAction, useCallback, useEffect, useRef, useState } from "react";
|
3
3
|
import { GetLogger } from "../_modules/config";
|
4
4
|
|
5
|
-
const logger = GetLogger("helpers/hooks");
|
6
5
|
/** Empty array ensures that effect is only run on mount */
|
7
6
|
export const useEffectOnlyOnMount = [];
|
8
7
|
|
9
8
|
/** set state on steroids. provide promise callback after render, onChange transformer and automatic skip-set when value not changed */
|
10
9
|
export function useStateEX<ValueType>(initialValue: ValueType, options?: {
|
11
|
-
onChange?: (newValue: SetStateAction<ValueType
|
10
|
+
onChange?: (newValue: SetStateAction<ValueType>, isValueChanged: boolean) => SetStateAction<ValueType>;
|
12
11
|
//will not set state if value did not change
|
13
12
|
skipUpdateIfSame?: boolean;
|
14
13
|
//optional, provide a name for better logging
|
@@ -53,26 +52,36 @@ export function useStateEX<ValueType>(initialValue: ValueType, options?: {
|
|
53
52
|
}
|
54
53
|
}, [value, resolveState.current]);
|
55
54
|
|
56
|
-
|
57
|
-
logger.groupSync('
|
55
|
+
function getIsValueChanged(newValue: ValueType): boolean {
|
56
|
+
return logger.groupSync('getIsValueChanged', log => {
|
58
57
|
if (logger.getLevel() === LoggerLevel.VERBOSE) {
|
59
58
|
log('old: ' + jsonStringify(currentValue.current));
|
60
59
|
log('new: ' + jsonStringify(newValue));
|
61
60
|
}
|
62
61
|
if (!objectsEqual(newValue as object, currentValue.current as object)) {
|
63
62
|
log(`value changed`);
|
64
|
-
|
63
|
+
return true;
|
65
64
|
}
|
66
65
|
else {
|
67
66
|
log(`value unchanged`);
|
68
|
-
|
67
|
+
return false;
|
69
68
|
}
|
70
69
|
});
|
70
|
+
};
|
71
|
+
|
72
|
+
let setValueWithCheck = !options.skipUpdateIfSame ? setValueInState : (newValue: ValueType) => {
|
73
|
+
const isValueChanged = getIsValueChanged(newValue);
|
74
|
+
if (isValueChanged) {
|
75
|
+
setValueInState(newValue);
|
76
|
+
}
|
77
|
+
else {
|
78
|
+
resolvePromises();
|
79
|
+
}
|
71
80
|
}
|
72
81
|
|
73
82
|
|
74
83
|
let setValueWithEvents = wrapFunction(setValueWithCheck, {
|
75
|
-
before: (newValue: ValueType) => isFunction(options.onChange) ? options.onChange(newValue) : newValue,
|
84
|
+
before: (newValue: ValueType) => isFunction(options.onChange) ? options.onChange(newValue, getIsValueChanged(newValue)) : newValue,
|
76
85
|
after: (newValue: ValueType) => currentValue.current = isPrimitiveValue(newValue) || isFunction(newValue)
|
77
86
|
? newValue
|
78
87
|
//fix skipUpdateIfSame for complex objects
|
package/src/styles/styles.ts
CHANGED
@@ -58,6 +58,8 @@ export module mixins {
|
|
58
58
|
|
59
59
|
export const KnownClassNames = {
|
60
60
|
print: 'print-root',
|
61
|
+
printHide: 'print-hide',
|
62
|
+
printShow: 'print-show',
|
61
63
|
section: 'kfui-section',
|
62
64
|
vertical: 'kfui-vertical',
|
63
65
|
horizontal: 'kfui-horizontal',
|
@@ -67,26 +69,11 @@ export const KnownClassNames = {
|
|
67
69
|
accordionBody: 'kfui-accordion-body',
|
68
70
|
accordionBodyWrapper: 'kfui-accordion-body-wrapper',
|
69
71
|
isOpen: 'is-opened',
|
70
|
-
progressBarStepLabel: 'step-label'
|
72
|
+
progressBarStepLabel: 'step-label',
|
73
|
+
left: 'float-left',
|
74
|
+
right: 'float-right'
|
71
75
|
}
|
72
76
|
export const useCommonStyles = makeStyles({
|
73
|
-
printShow: {
|
74
|
-
display: 'none',
|
75
|
-
[`:global(body.${KnownClassNames.print})`]: {
|
76
|
-
display: 'unset',
|
77
|
-
},
|
78
|
-
'@media print': {
|
79
|
-
display: 'unset',
|
80
|
-
}
|
81
|
-
},
|
82
|
-
printHide: {
|
83
|
-
[`:global(body.${KnownClassNames.print})`]: {
|
84
|
-
display: 'none !important'
|
85
|
-
},
|
86
|
-
'@media print': {
|
87
|
-
display: 'none !important'
|
88
|
-
}
|
89
|
-
},
|
90
77
|
hintLabel: {
|
91
78
|
color: tokens.colorNeutralForeground3,
|
92
79
|
fontSize: tokens.fontSizeBase200,
|