@linzjs/windows 8.8.2 → 9.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/dist/common/index.ts +1 -0
- package/dist/common/util.ts +31 -0
- package/dist/index.ts +1 -0
- package/dist/panel/Panel.tsx +4 -4
- package/dist/panel/PanelInline.tsx +40 -0
- package/dist/panel/PanelInstanceContext.ts +0 -2
- package/dist/panel/PanelInstanceContextProvider.tsx +0 -1
- package/dist/panel/PanelsContextProvider.tsx +41 -36
- package/dist/panel/index.ts +1 -1
- package/dist/ribbon/Ribbon.scss +146 -0
- package/dist/ribbon/RibbonButton.tsx +95 -0
- package/dist/ribbon/RibbonButtonLink.tsx +26 -0
- package/dist/ribbon/RibbonButtonOpenPanel.tsx +30 -0
- package/dist/ribbon/RibbonButtonSliderContext.tsx +30 -0
- package/dist/ribbon/RibbonContainer.tsx +20 -0
- package/dist/ribbon/RibbonDeprecated.tsx +27 -0
- package/dist/ribbon/RibbonMenu.scss +45 -0
- package/dist/ribbon/RibbonMenu.tsx +25 -0
- package/dist/ribbon/RibbonMenuOption.tsx +67 -0
- package/dist/ribbon/RibbonMenuSeparator.tsx +5 -0
- package/dist/ribbon/RibbonSeparator.tsx +3 -0
- package/dist/ribbon/RibbonSliderButton.tsx +63 -0
- package/dist/ribbon/index.ts +11 -0
- package/package.json +24 -23
- package/dist/panel/OpenPanelIcon.scss +0 -72
- package/dist/panel/OpenPanelIcon.tsx +0 -60
package/dist/common/index.ts
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const tabToNextSiblingElement = (): void => tabToSiblingElement(1);
|
|
2
|
+
export const tabToPreviousSiblingElement = (): void => tabToSiblingElement(-1);
|
|
3
|
+
|
|
4
|
+
export const tabToSiblingElement = (direction: 1 | -1): void => {
|
|
5
|
+
const active = document.activeElement as HTMLElement | null;
|
|
6
|
+
if (!active) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const selector = [
|
|
11
|
+
'a[href]',
|
|
12
|
+
'area[href]',
|
|
13
|
+
'button:not([disabled])',
|
|
14
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
15
|
+
'select:not([disabled])',
|
|
16
|
+
'textarea:not([disabled])',
|
|
17
|
+
'iframe',
|
|
18
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
19
|
+
'[contenteditable="true"]',
|
|
20
|
+
].join(',');
|
|
21
|
+
|
|
22
|
+
const focusables = Array.from(active.parentElement?.querySelectorAll<HTMLElement>(selector) ?? []).filter(
|
|
23
|
+
(el) => el.offsetParent !== null && getComputedStyle(el).visibility !== 'hidden',
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const next = focusables.indexOf(active) + direction;
|
|
27
|
+
|
|
28
|
+
if (next >= 0 && next < focusables.length) {
|
|
29
|
+
focusables[next]?.focus();
|
|
30
|
+
}
|
|
31
|
+
};
|
package/dist/index.ts
CHANGED
package/dist/panel/Panel.tsx
CHANGED
|
@@ -63,7 +63,7 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const pos = props.position;
|
|
66
|
-
if (savedState?.panelPosition) {
|
|
66
|
+
if (props.resizeable !== false && savedState?.panelPosition) {
|
|
67
67
|
return savedState.panelPosition;
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -100,7 +100,7 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
100
100
|
return pick(result, ['width', 'height']);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
if (savedState?.panelSize) {
|
|
103
|
+
if (props.resizeable !== false && savedState?.panelSize) {
|
|
104
104
|
return cropSizeToWindow(savedState.panelSize);
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -117,13 +117,13 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
117
117
|
const setInitialPanelSize = useCallback(
|
|
118
118
|
(size: PanelSize) => {
|
|
119
119
|
// If panel was already sized from state, then don't resize
|
|
120
|
-
if (savedState?.panelSize) {
|
|
120
|
+
if (props.resizeable !== false && savedState?.panelSize) {
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
setPanelSize(size);
|
|
125
125
|
},
|
|
126
|
-
[savedState?.panelSize, setPanelSize],
|
|
126
|
+
[props.resizeable, savedState?.panelSize, setPanelSize],
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
const dynamicResize = useCallback(() => {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { v4 } from 'uuid';
|
|
3
|
+
|
|
4
|
+
import { PanelContext } from './PanelContext';
|
|
5
|
+
import { PanelInstanceContext } from './PanelInstanceContext';
|
|
6
|
+
|
|
7
|
+
export interface PanelInlineProps {
|
|
8
|
+
title: string;
|
|
9
|
+
width: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const PanelInline = ({ title, width, children }: PropsWithChildren<PanelInlineProps>) => {
|
|
13
|
+
return (
|
|
14
|
+
<PanelContext.Provider value={{ resizeable: false, resizePanel: () => {}, initialResizePanel: () => {} }}>
|
|
15
|
+
<PanelInstanceContext.Provider
|
|
16
|
+
value={{
|
|
17
|
+
setTitle: () => {},
|
|
18
|
+
title,
|
|
19
|
+
zIndex: 1500,
|
|
20
|
+
uniqueId: v4(),
|
|
21
|
+
dock: () => {},
|
|
22
|
+
panelClose: () => {},
|
|
23
|
+
dockId: undefined,
|
|
24
|
+
docked: false,
|
|
25
|
+
bringPanelToFront: () => {},
|
|
26
|
+
hidden: false,
|
|
27
|
+
setPanelWindow: () => {},
|
|
28
|
+
panelTogglePopout: () => {},
|
|
29
|
+
undock: () => {},
|
|
30
|
+
panelPoppedOut: false,
|
|
31
|
+
bounds: undefined,
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
<div className={'WindowPanel'} title={title} style={{ width }}>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
</PanelInstanceContext.Provider>
|
|
38
|
+
</PanelContext.Provider>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -4,7 +4,6 @@ export interface PanelInstanceContextType {
|
|
|
4
4
|
title: string;
|
|
5
5
|
setTitle: (title: string) => void;
|
|
6
6
|
uniqueId: string;
|
|
7
|
-
panelName: string;
|
|
8
7
|
bounds: string | Element | undefined;
|
|
9
8
|
panelTogglePopout: () => void;
|
|
10
9
|
panelClose: () => void;
|
|
@@ -29,7 +28,6 @@ const NoContextError = () => {
|
|
|
29
28
|
export const PanelInstanceContext = createContext<PanelInstanceContextType>({
|
|
30
29
|
title: 'Missing PanelInstanceContext Provider: title',
|
|
31
30
|
uniqueId: 'Missing PanelInstanceContext Provider: uniqueId',
|
|
32
|
-
panelName: 'Missing PanelInstanceContext Provider: panelName',
|
|
33
31
|
bounds: document.body,
|
|
34
32
|
setTitle: NoContextError,
|
|
35
33
|
panelTogglePopout: NoContextError,
|
|
@@ -51,24 +51,28 @@ export const PanelsContextProvider = ({
|
|
|
51
51
|
|
|
52
52
|
const bringPanelToFront = useCallback(
|
|
53
53
|
(panelUniqueId: string) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
panelInstance.window
|
|
61
|
-
|
|
54
|
+
setPanelInstances((panelInstances): PanelInstance[] => {
|
|
55
|
+
const panelInstance = panelInstances.find((panelInstance) => panelInstance.uniqueId === panelUniqueId);
|
|
56
|
+
if (!panelInstance) {
|
|
57
|
+
console.warn(`bringPanelToFront cannot find panel with uniqueId: ${panelUniqueId}`);
|
|
58
|
+
return panelInstances;
|
|
59
|
+
}
|
|
60
|
+
if (panelInstance.window) {
|
|
61
|
+
panelInstance.window.focus();
|
|
62
|
+
return panelInstances;
|
|
63
|
+
}
|
|
62
64
|
const maxZIndexPanelInstance = maxBy(panelInstances, 'zIndex');
|
|
63
65
|
// Prevent unnecessary state updates
|
|
64
|
-
if (maxZIndexPanelInstance === panelInstance)
|
|
66
|
+
if (maxZIndexPanelInstance === panelInstance) {
|
|
67
|
+
return panelInstances;
|
|
68
|
+
}
|
|
65
69
|
sortBy(panelInstances, (pi) => (panelInstance === pi ? 32768 : pi.zIndex)).forEach(
|
|
66
70
|
(pi, i) => (pi.zIndex = baseZIndex + i),
|
|
67
71
|
);
|
|
68
|
-
|
|
69
|
-
}
|
|
72
|
+
return [...panelInstances];
|
|
73
|
+
});
|
|
70
74
|
},
|
|
71
|
-
[baseZIndex
|
|
75
|
+
[baseZIndex],
|
|
72
76
|
);
|
|
73
77
|
|
|
74
78
|
const showHidePanel = useCallback((hidePanelIds: string | string[], hidden: boolean) => {
|
|
@@ -104,39 +108,40 @@ export const PanelsContextProvider = ({
|
|
|
104
108
|
const openPanel = useCallback(
|
|
105
109
|
({ componentFn, poppedOut = false, uniqueId = v4(), onClose }: OpenPanelOptions): string | null => {
|
|
106
110
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (existingPanelInstance
|
|
110
|
-
existingPanelInstance.window
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
setPanelInstances((panelInstances) => {
|
|
112
|
+
const existingPanelInstance = panelInstances.find((pi) => pi.uniqueId === uniqueId);
|
|
113
|
+
if (existingPanelInstance) {
|
|
114
|
+
if (existingPanelInstance.window) {
|
|
115
|
+
existingPanelInstance.window?.focus();
|
|
116
|
+
} else {
|
|
117
|
+
if (existingPanelInstance.hidden) {
|
|
118
|
+
setTimeout(() => unhidePanel(uniqueId), 0);
|
|
119
|
+
}
|
|
120
|
+
setTimeout(() => bringPanelToFront(uniqueId), 0);
|
|
114
121
|
}
|
|
115
|
-
|
|
122
|
+
return panelInstances;
|
|
116
123
|
}
|
|
117
|
-
return existingPanelInstance.uniqueId;
|
|
118
|
-
}
|
|
119
124
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
return [
|
|
126
|
+
...panelInstances,
|
|
127
|
+
{
|
|
128
|
+
uniqueId,
|
|
129
|
+
componentInstance: componentFn(),
|
|
130
|
+
zIndex: baseZIndex + panelInstances.length,
|
|
131
|
+
poppedOut,
|
|
132
|
+
window: null,
|
|
133
|
+
onClose,
|
|
134
|
+
hidden: false,
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
});
|
|
133
138
|
return uniqueId;
|
|
134
139
|
} catch (e) {
|
|
135
140
|
console.error(e);
|
|
136
141
|
return null;
|
|
137
142
|
}
|
|
138
143
|
},
|
|
139
|
-
[baseZIndex, bringPanelToFront,
|
|
144
|
+
[baseZIndex, bringPanelToFront, unhidePanel],
|
|
140
145
|
);
|
|
141
146
|
|
|
142
147
|
const closePanel = useCallback((closePanelUniqueIds: string | string[]) => {
|
package/dist/panel/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from './OpenPanelButton';
|
|
2
|
-
export * from './OpenPanelIcon';
|
|
3
2
|
export * from './Panel';
|
|
4
3
|
export * from './PanelContext';
|
|
5
4
|
export * from './PanelDock';
|
|
6
5
|
export * from './PanelHeader';
|
|
7
6
|
export * from './PanelHeaderButton';
|
|
7
|
+
export * from './PanelInline';
|
|
8
8
|
export * from './PanelInstanceContext';
|
|
9
9
|
export * from './PanelInstanceContextProvider';
|
|
10
10
|
export * from './PanelsContext';
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
@use "@linzjs/lui/dist/scss/Core" as lui;
|
|
2
|
+
|
|
3
|
+
.RibbonButton {
|
|
4
|
+
position: relative;
|
|
5
|
+
background-color: transparent;
|
|
6
|
+
padding: 4px;
|
|
7
|
+
margin: 2px;
|
|
8
|
+
fill: lui.$sea;
|
|
9
|
+
color: lui.$sea;
|
|
10
|
+
width: 40px;
|
|
11
|
+
height: 40px;
|
|
12
|
+
border: 2px solid white;
|
|
13
|
+
border-radius: 5px;
|
|
14
|
+
|
|
15
|
+
&:hover {
|
|
16
|
+
background-color: lui.$polar;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
span.LuiIcon {
|
|
20
|
+
display: block;
|
|
21
|
+
margin: auto;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.RibbonButtonSkeleton {
|
|
26
|
+
padding: 4px;
|
|
27
|
+
margin: 0;
|
|
28
|
+
line-height: 1px;
|
|
29
|
+
|
|
30
|
+
> div {
|
|
31
|
+
padding: 0;
|
|
32
|
+
margin: 0;
|
|
33
|
+
border: 2px solid transparent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.RibbonButton-selected, .RibbonButton:has(+ *:popover-open) {
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
color: lui.$white !important;
|
|
40
|
+
background-color: lui.$sea !important;
|
|
41
|
+
box-shadow: inset 0 2px 4px rgb(41 92 130);
|
|
42
|
+
|
|
43
|
+
svg path {
|
|
44
|
+
color: lui.$white !important;
|
|
45
|
+
fill: lui.$white !important;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.RibbonButton-disabled {
|
|
50
|
+
background-color: lui.$white !important;
|
|
51
|
+
|
|
52
|
+
fill: lui.$grey-20 !important;
|
|
53
|
+
|
|
54
|
+
svg * {
|
|
55
|
+
fill: lui.$grey-20 !important;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
%RibbonButton-Group {
|
|
60
|
+
background-color: white;
|
|
61
|
+
border-radius: 5px;
|
|
62
|
+
padding: 0;
|
|
63
|
+
align-items: center;
|
|
64
|
+
box-shadow: 0 0 10px rgb(0 0 0 / 20%);
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.RibbonButtonContent {
|
|
69
|
+
@extend %RibbonButton-Group;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.RibbonButton-separator {
|
|
73
|
+
background-color: lui.$grey-10;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.RibbonButton-verticalGroup {
|
|
77
|
+
@extend %RibbonButton-Group;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
|
|
80
|
+
.RibbonButton-separator {
|
|
81
|
+
margin-top: 1px;
|
|
82
|
+
margin-bottom: 1px;
|
|
83
|
+
height: 2px;
|
|
84
|
+
width: 44px;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.RibbonButton-horizontalGroup {
|
|
89
|
+
@extend %RibbonButton-Group;
|
|
90
|
+
flex-direction: row;
|
|
91
|
+
|
|
92
|
+
.RibbonButton-separator {
|
|
93
|
+
margin-left: 1px;
|
|
94
|
+
margin-right: 1px;
|
|
95
|
+
height: 44px;
|
|
96
|
+
width: 2px;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.RibbonSliderMenu[popover] {
|
|
101
|
+
margin: 0;
|
|
102
|
+
border: 0;
|
|
103
|
+
inset: auto;
|
|
104
|
+
background: transparent;
|
|
105
|
+
position: fixed;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.RibbonSliderMenu:popover-open {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: row;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
$RibbonSliderMenu-offset1: 2px;
|
|
114
|
+
$RibbonSliderMenu-offset2: 6px;
|
|
115
|
+
|
|
116
|
+
.RibbonSliderMenu-right, .RibbonSliderMenu-right-down {
|
|
117
|
+
translate: ($RibbonSliderMenu-offset1) (-$RibbonSliderMenu-offset2);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.RibbonSliderMenu-left, .RibbonSliderMenu-left-down {
|
|
121
|
+
translate: (-$RibbonSliderMenu-offset1) (-$RibbonSliderMenu-offset2);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.RibbonSliderMenu-right-up {
|
|
125
|
+
translate: ($RibbonSliderMenu-offset1) ($RibbonSliderMenu-offset2);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.RibbonSliderMenu-left-up {
|
|
129
|
+
translate: (-$RibbonSliderMenu-offset1) ($RibbonSliderMenu-offset2);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.RibbonSliderMenu-up, .RibbonSliderMenu-up-right {
|
|
133
|
+
translate: (-$RibbonSliderMenu-offset2) (-$RibbonSliderMenu-offset1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.RibbonSliderMenu-down-left {
|
|
137
|
+
translate: ($RibbonSliderMenu-offset2) ($RibbonSliderMenu-offset1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.RibbonSliderMenu-down, .RibbonSliderMenu-down-right {
|
|
141
|
+
translate: (-$RibbonSliderMenu-offset2) ($RibbonSliderMenu-offset1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.RibbonSliderMenu-down-left {
|
|
145
|
+
translate: ($RibbonSliderMenu-offset2) ($RibbonSliderMenu-offset1);
|
|
146
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import './Ribbon.scss';
|
|
2
|
+
|
|
3
|
+
import { LuiIcon } from '@linzjs/lui';
|
|
4
|
+
import { IconName } from '@linzjs/lui/dist/components/LuiIcon/LuiIcon';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import { forwardRef, ReactElement, useContext } from 'react';
|
|
7
|
+
import Skeleton from 'react-loading-skeleton';
|
|
8
|
+
|
|
9
|
+
import { tabToNextSiblingElement, tabToPreviousSiblingElement } from '../common';
|
|
10
|
+
import { RibbonButtonSliderContext } from './RibbonButtonSliderContext';
|
|
11
|
+
|
|
12
|
+
export interface RibbonButtonProps {
|
|
13
|
+
id?: string;
|
|
14
|
+
popoverTarget?: string;
|
|
15
|
+
popoverTargetAction?: 'toggle' | 'show' | 'hide' | undefined;
|
|
16
|
+
anchorName?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
icon?: IconName;
|
|
19
|
+
content?: ReactElement;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
selected?: boolean;
|
|
22
|
+
loading?: boolean;
|
|
23
|
+
className?: string;
|
|
24
|
+
testId?: string;
|
|
25
|
+
onClick?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const RibbonButton = forwardRef<HTMLButtonElement, RibbonButtonProps>(function RibbonButton(
|
|
29
|
+
{
|
|
30
|
+
id,
|
|
31
|
+
popoverTarget,
|
|
32
|
+
popoverTargetAction,
|
|
33
|
+
anchorName,
|
|
34
|
+
title,
|
|
35
|
+
icon,
|
|
36
|
+
content,
|
|
37
|
+
className,
|
|
38
|
+
selected,
|
|
39
|
+
loading,
|
|
40
|
+
disabled,
|
|
41
|
+
testId,
|
|
42
|
+
onClick,
|
|
43
|
+
},
|
|
44
|
+
ref,
|
|
45
|
+
) {
|
|
46
|
+
const { menuUniqueId, autoClose } = useContext(RibbonButtonSliderContext);
|
|
47
|
+
|
|
48
|
+
if (loading) {
|
|
49
|
+
return (
|
|
50
|
+
<div className={'RibbonButtonSkeleton'}>
|
|
51
|
+
<Skeleton
|
|
52
|
+
className={clsx(
|
|
53
|
+
disabled && 'disabled', // disabled to style cursor
|
|
54
|
+
)}
|
|
55
|
+
height={36}
|
|
56
|
+
width={36}
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<button
|
|
64
|
+
ref={ref}
|
|
65
|
+
id={id}
|
|
66
|
+
style={{ anchorName }}
|
|
67
|
+
{...((popoverTarget || menuUniqueId) && { ['popovertarget' as never]: popoverTarget ?? menuUniqueId })}
|
|
68
|
+
{...((popoverTargetAction || autoClose != null) && {
|
|
69
|
+
['popovertargetaction' as never]: popoverTargetAction ?? (autoClose ? 'toggle' : 'show'),
|
|
70
|
+
})}
|
|
71
|
+
type="button"
|
|
72
|
+
title={title}
|
|
73
|
+
data-testid={testId}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
onClick={onClick}
|
|
76
|
+
className={clsx(
|
|
77
|
+
className,
|
|
78
|
+
'RibbonButton',
|
|
79
|
+
selected && 'RibbonButton-selected',
|
|
80
|
+
disabled && 'RibbonButton-disabled',
|
|
81
|
+
)}
|
|
82
|
+
onKeyDown={({ code }) => {
|
|
83
|
+
if (code === 'ArrowDown' || code === 'ArrowRight') {
|
|
84
|
+
tabToNextSiblingElement();
|
|
85
|
+
}
|
|
86
|
+
if (code === 'ArrowUp' || code === 'ArrowLeft') {
|
|
87
|
+
tabToPreviousSiblingElement();
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{icon && <LuiIcon name={icon} alt={title ?? ''} size={'md'} />}
|
|
92
|
+
{content}
|
|
93
|
+
</button>
|
|
94
|
+
);
|
|
95
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import './Ribbon.scss';
|
|
2
|
+
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
|
|
5
|
+
import { RibbonButton, RibbonButtonProps } from './RibbonButton';
|
|
6
|
+
|
|
7
|
+
export type RibbonButtonLinkProps = RibbonButtonProps & {
|
|
8
|
+
href: string;
|
|
9
|
+
target?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const RibbonButtonLink = forwardRef<HTMLButtonElement, RibbonButtonLinkProps>(function RibbonButtonLink(
|
|
13
|
+
{ onClick, href, target = '_blank', ...props },
|
|
14
|
+
ref,
|
|
15
|
+
) {
|
|
16
|
+
return (
|
|
17
|
+
<RibbonButton
|
|
18
|
+
ref={ref}
|
|
19
|
+
{...props}
|
|
20
|
+
onClick={() => {
|
|
21
|
+
onClick?.();
|
|
22
|
+
void window.open(href, target);
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import './Ribbon.scss';
|
|
2
|
+
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { forwardRef, useContext } from 'react';
|
|
5
|
+
|
|
6
|
+
import { OpenPanelOptions, PanelsContext } from '../panel/PanelsContext';
|
|
7
|
+
import { RibbonButton, RibbonButtonProps } from './RibbonButton';
|
|
8
|
+
|
|
9
|
+
export type RibbonButtonOpenPanelProps = Omit<RibbonButtonProps, 'onClick'> & OpenPanelOptions;
|
|
10
|
+
export const RibbonButtonOpenPanel = forwardRef<HTMLButtonElement, RibbonButtonOpenPanelProps>(
|
|
11
|
+
function RibbonButtonOpenPanel(
|
|
12
|
+
{ title, icon, className, disabled, testId, uniqueId = title, loading, ...openPanelOptions },
|
|
13
|
+
ref,
|
|
14
|
+
) {
|
|
15
|
+
const { openPanel, openPanels } = useContext(PanelsContext);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<RibbonButton
|
|
19
|
+
ref={ref}
|
|
20
|
+
testId={testId}
|
|
21
|
+
icon={icon}
|
|
22
|
+
title={title}
|
|
23
|
+
disabled={disabled}
|
|
24
|
+
loading={loading}
|
|
25
|
+
onClick={() => openPanel({ uniqueId, ...openPanelOptions })}
|
|
26
|
+
className={clsx(openPanels.has(uniqueId ?? '') && 'RibbonButton-selected', className)}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
},
|
|
30
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
export const ribbonSliderAlignments = {
|
|
4
|
+
right: ['left', 'right', 'top'],
|
|
5
|
+
'right-down': ['left', 'right', 'top'],
|
|
6
|
+
'right-up': ['left', 'right', 'bottom'],
|
|
7
|
+
left: ['right', 'left', 'top'],
|
|
8
|
+
'left-down': ['right', 'left', 'top'],
|
|
9
|
+
'left-up': ['right', 'left', 'bottom'],
|
|
10
|
+
'down-left': ['top', 'bottom', 'right'],
|
|
11
|
+
'down-right': ['top', 'bottom', 'left'],
|
|
12
|
+
down: ['top', 'bottom', 'left'],
|
|
13
|
+
'up-left': ['bottom', 'top', 'right'],
|
|
14
|
+
'up-right': ['bottom', 'top', 'left'],
|
|
15
|
+
up: ['bottom', 'top', 'left'],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type RibbonSliderAlignment = keyof typeof ribbonSliderAlignments;
|
|
19
|
+
|
|
20
|
+
export interface RibbonButtonSliderContextType {
|
|
21
|
+
autoClose: boolean | undefined;
|
|
22
|
+
menuUniqueId: string;
|
|
23
|
+
positionAnchor: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const RibbonButtonSliderContext = createContext<RibbonButtonSliderContextType>({
|
|
27
|
+
autoClose: undefined,
|
|
28
|
+
menuUniqueId: '',
|
|
29
|
+
positionAnchor: '',
|
|
30
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import './Ribbon.scss';
|
|
2
|
+
|
|
3
|
+
import { CSSProperties, PropsWithChildren } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface RibbonContainerProps {
|
|
6
|
+
orientation?: 'horizontal' | 'vertical';
|
|
7
|
+
style?: CSSProperties;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const RibbonContainer = ({
|
|
12
|
+
orientation = 'horizontal',
|
|
13
|
+
style,
|
|
14
|
+
className,
|
|
15
|
+
children,
|
|
16
|
+
}: PropsWithChildren<RibbonContainerProps>) => (
|
|
17
|
+
<div style={style} className={className}>
|
|
18
|
+
<div className={`RibbonButton-${orientation}Group`}>{children}</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
import { RibbonButtonProps } from './RibbonButton';
|
|
4
|
+
import { RibbonButtonOpenPanel, RibbonButtonOpenPanelProps } from './RibbonButtonOpenPanel';
|
|
5
|
+
import { RibbonContainer } from './RibbonContainer';
|
|
6
|
+
import { RibbonSeparator } from './RibbonSeparator';
|
|
7
|
+
|
|
8
|
+
// @Deprecated use RibbonPanelButton
|
|
9
|
+
export type OpenPanelIconProps = RibbonButtonProps;
|
|
10
|
+
|
|
11
|
+
// @Deprecated use RibbonPanelButton
|
|
12
|
+
export const OpenPanelIcon = ({ iconTitle, ...props }: RibbonButtonOpenPanelProps & { iconTitle?: string }) => (
|
|
13
|
+
<RibbonButtonOpenPanel title={iconTitle} {...props} />
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
// @Deprecated use RibbonContainer
|
|
17
|
+
export const ButtonIconHorizontalGroup = (props: PropsWithChildren) => (
|
|
18
|
+
<RibbonContainer orientation={'horizontal'} {...props} />
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// @Deprecated use RibbonContainer
|
|
22
|
+
export const ButtonIconVerticalGroup = (props: PropsWithChildren) => (
|
|
23
|
+
<RibbonContainer orientation={'vertical'} {...props} />
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// @Deprecated use ButtonIconSeparator
|
|
27
|
+
export const ButtonIconSeparator = RibbonSeparator;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@use "@linzjs/lui/dist/scss/Core" as lui;
|
|
2
|
+
|
|
3
|
+
.RibbonMenuSeparator {
|
|
4
|
+
height: 1px;
|
|
5
|
+
background-color: lui.$dew;
|
|
6
|
+
margin: 4px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.RibbonMenuOption {
|
|
10
|
+
font-family: "Open Sans", system-ui, sans-serif;
|
|
11
|
+
color: lui.$charcoal;
|
|
12
|
+
|
|
13
|
+
path {
|
|
14
|
+
fill: lui.$fuscous;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
min-width: 180px;
|
|
18
|
+
display: flex;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
background-color: transparent;
|
|
21
|
+
font-weight: 400;
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
border: 1px transparent;
|
|
24
|
+
outline: none;
|
|
25
|
+
padding: 6px 12px;
|
|
26
|
+
line-height: 24px;
|
|
27
|
+
font-size: 16px;
|
|
28
|
+
align-items: center;;
|
|
29
|
+
|
|
30
|
+
&:hover {
|
|
31
|
+
background-color: lui.$polar;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:focus {
|
|
35
|
+
background-color: lui.$polar;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&:disabled {
|
|
39
|
+
color: lui.$disabled-color;
|
|
40
|
+
|
|
41
|
+
path {
|
|
42
|
+
fill: lui.$disabled-color;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createContext, PropsWithChildren, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { RibbonContainer } from './RibbonContainer';
|
|
4
|
+
|
|
5
|
+
export interface RibbonMenuContext {
|
|
6
|
+
hasIcon: boolean;
|
|
7
|
+
setHasIcon: (hasIcon: boolean) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const RibbonMenuContext = createContext<RibbonMenuContext>({
|
|
11
|
+
hasIcon: false,
|
|
12
|
+
setHasIcon: () => {},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const RibbonMenu = ({ children }: PropsWithChildren) => {
|
|
16
|
+
const [hasIcon, setHasIcon] = useState(false);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<RibbonMenuContext.Provider value={{ hasIcon, setHasIcon }}>
|
|
20
|
+
<RibbonContainer orientation={'vertical'}>
|
|
21
|
+
<div style={{ padding: '4px 0' }}>{children}</div>
|
|
22
|
+
</RibbonContainer>
|
|
23
|
+
</RibbonMenuContext.Provider>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import './RibbonMenu.scss';
|
|
2
|
+
|
|
3
|
+
import { LuiIcon } from '@linzjs/lui';
|
|
4
|
+
import { IconName } from '@linzjs/lui/dist/components/LuiIcon/LuiIcon';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import { forwardRef, PropsWithChildren, useContext, useEffect } from 'react';
|
|
7
|
+
|
|
8
|
+
import { tabToNextSiblingElement, tabToPreviousSiblingElement } from '../common/util';
|
|
9
|
+
import { RibbonButtonSliderContext } from './RibbonButtonSliderContext';
|
|
10
|
+
import { RibbonMenuContext } from './RibbonMenu';
|
|
11
|
+
|
|
12
|
+
export interface RibbonMenuOption {
|
|
13
|
+
id?: string;
|
|
14
|
+
popoverTarget?: string;
|
|
15
|
+
popoverTargetAction?: 'toggle' | 'show' | 'hide' | undefined;
|
|
16
|
+
anchorName?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
icon?: IconName;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
className?: string;
|
|
21
|
+
testId?: string;
|
|
22
|
+
onClick?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const RibbonMenuOption = forwardRef<HTMLButtonElement, PropsWithChildren<RibbonMenuOption>>(
|
|
26
|
+
function RibbonButton(
|
|
27
|
+
{ id, popoverTarget, popoverTargetAction, anchorName, title, icon, children, className, disabled, testId, onClick },
|
|
28
|
+
ref,
|
|
29
|
+
) {
|
|
30
|
+
const { menuUniqueId, autoClose } = useContext(RibbonButtonSliderContext);
|
|
31
|
+
const { hasIcon, setHasIcon } = useContext(RibbonMenuContext);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setHasIcon(!!icon);
|
|
35
|
+
}, [icon, setHasIcon]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<button
|
|
39
|
+
ref={ref}
|
|
40
|
+
id={id}
|
|
41
|
+
style={{ anchorName }}
|
|
42
|
+
{...{ ['popovertarget' as never]: popoverTarget ?? menuUniqueId }}
|
|
43
|
+
{...{ ['popovertargetaction' as never]: popoverTargetAction ?? (autoClose ? 'toggle' : 'show') }}
|
|
44
|
+
type="button"
|
|
45
|
+
title={title}
|
|
46
|
+
data-testid={testId}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
onKeyDown={({ code }) => {
|
|
49
|
+
if (code === 'ArrowDown') {
|
|
50
|
+
tabToNextSiblingElement();
|
|
51
|
+
}
|
|
52
|
+
if (code === 'ArrowUp') {
|
|
53
|
+
tabToPreviousSiblingElement();
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
onMouseMove={({ currentTarget }) => {
|
|
57
|
+
currentTarget?.focus?.();
|
|
58
|
+
}}
|
|
59
|
+
onClick={onClick}
|
|
60
|
+
className={clsx(className, 'RibbonMenuOption', disabled && 'RibbonMenuOption-disabled')}
|
|
61
|
+
>
|
|
62
|
+
<div style={{ display: 'flex' }}>{icon && <LuiIcon name={icon} alt={title ?? ''} size={'md'} />}</div>
|
|
63
|
+
<div style={hasIcon && !icon ? { paddingLeft: 24 } : {}}>{children}</div>
|
|
64
|
+
</button>
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import './Ribbon.scss';
|
|
2
|
+
|
|
3
|
+
import { PropsWithChildren, ReactElement, useState } from 'react';
|
|
4
|
+
import { v4 } from 'uuid';
|
|
5
|
+
|
|
6
|
+
import { RibbonButton, RibbonButtonProps } from './RibbonButton';
|
|
7
|
+
import { RibbonButtonSliderContext, RibbonSliderAlignment, ribbonSliderAlignments } from './RibbonButtonSliderContext';
|
|
8
|
+
|
|
9
|
+
export interface RibbonSliderProps extends RibbonButtonProps {
|
|
10
|
+
autoClose?: boolean;
|
|
11
|
+
alignment: RibbonSliderAlignment;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const RibbonButtonSlider = ({
|
|
15
|
+
alignment,
|
|
16
|
+
autoClose = false,
|
|
17
|
+
children,
|
|
18
|
+
onClick,
|
|
19
|
+
...buttonProps
|
|
20
|
+
}: PropsWithChildren<RibbonSliderProps>): ReactElement => {
|
|
21
|
+
const [uniqueId] = useState(v4());
|
|
22
|
+
const [menuUniqueId] = useState(`toolbarSliderMenu-${v4()}`);
|
|
23
|
+
const [positionAnchor] = useState(`--toolbarSliderAnchor-${uniqueId}`);
|
|
24
|
+
|
|
25
|
+
const defaultOnClick = () => setTimeout(focusNextRibbonButton, 0);
|
|
26
|
+
|
|
27
|
+
const alignments = ribbonSliderAlignments[alignment];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<RibbonButtonSliderContext.Provider value={{ autoClose, positionAnchor, menuUniqueId }}>
|
|
31
|
+
<RibbonButton
|
|
32
|
+
popoverTarget={menuUniqueId}
|
|
33
|
+
popoverTargetAction={'toggle'}
|
|
34
|
+
anchorName={positionAnchor}
|
|
35
|
+
{...buttonProps}
|
|
36
|
+
onClick={onClick ?? defaultOnClick}
|
|
37
|
+
/>
|
|
38
|
+
<nav
|
|
39
|
+
id={menuUniqueId}
|
|
40
|
+
{...{ ['popover' as never]: 'auto' }}
|
|
41
|
+
role="menu"
|
|
42
|
+
aria-label="Menu"
|
|
43
|
+
className={`RibbonSliderMenu RibbonSliderMenu-${alignment}`}
|
|
44
|
+
style={{
|
|
45
|
+
positionAnchor,
|
|
46
|
+
[alignments[0]]: `anchor(${positionAnchor} ${alignments[1]})`,
|
|
47
|
+
[alignments[2]]: `anchor(${positionAnchor} ${alignments[2]})`,
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</nav>
|
|
52
|
+
</RibbonButtonSliderContext.Provider>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const focusNextRibbonButton = () => {
|
|
57
|
+
let button =
|
|
58
|
+
document.activeElement?.nextElementSibling?.querySelectorAll<HTMLButtonElement>('button.RibbonButton-selected')[0];
|
|
59
|
+
if (!button) {
|
|
60
|
+
button = document.activeElement?.nextElementSibling?.querySelectorAll('button')[0];
|
|
61
|
+
}
|
|
62
|
+
button?.focus();
|
|
63
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './RibbonButton';
|
|
2
|
+
export * from './RibbonButtonLink';
|
|
3
|
+
export * from './RibbonButtonOpenPanel';
|
|
4
|
+
export * from './RibbonButtonSliderContext';
|
|
5
|
+
export * from './RibbonContainer';
|
|
6
|
+
export * from './RibbonDeprecated';
|
|
7
|
+
export * from './RibbonMenu';
|
|
8
|
+
export * from './RibbonMenuOption';
|
|
9
|
+
export * from './RibbonMenuSeparator';
|
|
10
|
+
export * from './RibbonSeparator';
|
|
11
|
+
export * from './RibbonSliderButton';
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"popout"
|
|
14
14
|
],
|
|
15
15
|
"main": "./dist/index.ts",
|
|
16
|
-
"version": "
|
|
16
|
+
"version": "9.0.0",
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"@linzjs/lui": ">=23",
|
|
19
19
|
"lodash-es": ">=4",
|
|
@@ -52,62 +52,63 @@
|
|
|
52
52
|
"@emotion/react": "^11.14.0",
|
|
53
53
|
"@emotion/styled": "11.14.1",
|
|
54
54
|
"@types/uuid": "^11.0.0",
|
|
55
|
-
"lodash-es": "
|
|
56
|
-
"react-
|
|
55
|
+
"lodash-es": "^4.17.23",
|
|
56
|
+
"react-loading-skeleton": "^3.5.0",
|
|
57
|
+
"react-rnd": "^10.5.3",
|
|
57
58
|
"usehooks-ts": "^3.1.1",
|
|
58
59
|
"uuid": "^13.0.0"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"@chromatic-com/storybook": "^4.1.3",
|
|
62
|
-
"@linzjs/lui": "^24.
|
|
63
|
+
"@linzjs/lui": "^24.10.1",
|
|
63
64
|
"@linzjs/step-ag-grid": "^29.14.1",
|
|
64
65
|
"@linzjs/style": "^5.4.0",
|
|
65
66
|
"@rollup/plugin-commonjs": "^28.0.9",
|
|
66
67
|
"@rollup/plugin-json": "^6.1.0",
|
|
67
68
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
68
|
-
"@storybook/addon-docs": "^9.1.
|
|
69
|
-
"@storybook/addon-links": "^9.1.
|
|
70
|
-
"@storybook/react-vite": "^9.1.
|
|
69
|
+
"@storybook/addon-docs": "^9.1.20",
|
|
70
|
+
"@storybook/addon-links": "^9.1.20",
|
|
71
|
+
"@storybook/react-vite": "^9.1.20",
|
|
71
72
|
"@testing-library/dom": "^10.4.1",
|
|
72
|
-
"@testing-library/react": "^16.3.
|
|
73
|
+
"@testing-library/react": "^16.3.2",
|
|
73
74
|
"@testing-library/user-event": "^14.6.1",
|
|
74
75
|
"@types/lodash-es": "^4.17.12",
|
|
75
76
|
"@types/node": "^22.19.2",
|
|
76
|
-
"@types/react": "^
|
|
77
|
-
"@types/react-dom": "^
|
|
78
|
-
"@vitejs/plugin-react-swc": "^4.
|
|
79
|
-
"@vitest/ui": "^4.0
|
|
77
|
+
"@types/react": "^18.3.28",
|
|
78
|
+
"@types/react-dom": "^18.3.7",
|
|
79
|
+
"@vitejs/plugin-react-swc": "^4.3.0",
|
|
80
|
+
"@vitest/ui": "^4.1.0",
|
|
80
81
|
"ag-grid-community": "34.2.0",
|
|
81
82
|
"ag-grid-react": "34.2.0",
|
|
82
83
|
"eslint-plugin-react": "^7.37.5",
|
|
83
|
-
"eslint-plugin-storybook": "^9.1.
|
|
84
|
+
"eslint-plugin-storybook": "^9.1.20",
|
|
84
85
|
"jsdom": "^27.3.0",
|
|
85
86
|
"mkdirp": "^3.0.1",
|
|
86
87
|
"npm-run-all": "^4.1.5",
|
|
87
|
-
"react": "18.3.1",
|
|
88
|
+
"react": "^18.3.1",
|
|
88
89
|
"react-app-polyfill": "^3.0.0",
|
|
89
90
|
"react-dom": "18.3.1",
|
|
90
|
-
"rollup": "^4.
|
|
91
|
+
"rollup": "^4.59.0",
|
|
91
92
|
"rollup-plugin-copy": "^3.5.0",
|
|
92
|
-
"sass": "^1.
|
|
93
|
-
"sass-loader": "^16.0.
|
|
94
|
-
"storybook": "^9.1.
|
|
93
|
+
"sass": "^1.98.0",
|
|
94
|
+
"sass-loader": "^16.0.7",
|
|
95
|
+
"storybook": "^9.1.19",
|
|
95
96
|
"style-loader": "^4.0.0",
|
|
96
97
|
"stylelint": "^16.26.1",
|
|
97
98
|
"stylelint-config-recommended": "^17.0.0",
|
|
98
99
|
"stylelint-config-recommended-scss": "^16.0.2",
|
|
99
100
|
"stylelint-config-standard": "^39.0.1",
|
|
100
101
|
"stylelint-prettier": "5.0.3",
|
|
101
|
-
"stylelint-scss": "6.
|
|
102
|
+
"stylelint-scss": "6.14.0",
|
|
102
103
|
"typescript": "^5.9.3",
|
|
103
|
-
"vite": "^7.
|
|
104
|
+
"vite": "^7.3.1",
|
|
104
105
|
"vite-plugin-html": "^3.2.2",
|
|
105
106
|
"vite-tsconfig-paths": "^5.1.4",
|
|
106
|
-
"vitest": "^4.0
|
|
107
|
+
"vitest": "^4.1.0"
|
|
107
108
|
},
|
|
108
109
|
"optionalDependencies": {
|
|
109
|
-
"@rollup/rollup-linux-x64-gnu": "^4.
|
|
110
|
-
"@swc/core-linux-x64-gnu": "^1.15.
|
|
110
|
+
"@rollup/rollup-linux-x64-gnu": "^4.59.0",
|
|
111
|
+
"@swc/core-linux-x64-gnu": "^1.15.18"
|
|
111
112
|
},
|
|
112
113
|
"browserslist": {
|
|
113
114
|
"production": [
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
@use "@linzjs/lui/dist/scss/Core" as lui;
|
|
2
|
-
|
|
3
|
-
.lui-button.lui-button-toolbar {
|
|
4
|
-
border-color: transparent;
|
|
5
|
-
padding: 4px;
|
|
6
|
-
line-height: 12px;
|
|
7
|
-
margin: 2px;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.OpenPanelIcon-selected {
|
|
11
|
-
cursor: pointer;
|
|
12
|
-
color: lui.$white !important;
|
|
13
|
-
background-color: lui.$blue-75 !important;
|
|
14
|
-
box-shadow: inset 0 2px 4px rgb(41 92 130);
|
|
15
|
-
|
|
16
|
-
fill: lui.$white !important;
|
|
17
|
-
|
|
18
|
-
svg * {
|
|
19
|
-
fill: lui.$white !important;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
svg * {
|
|
23
|
-
color: lui.$white !important;
|
|
24
|
-
fill: lui.$white !important;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.OpenPanelIcon-disabled {
|
|
29
|
-
background-color: lui.$white !important;
|
|
30
|
-
|
|
31
|
-
fill: lui.$grey-20 !important;
|
|
32
|
-
|
|
33
|
-
svg * {
|
|
34
|
-
fill: lui.$grey-20 !important;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
%OpenPanelIcon-Group {
|
|
39
|
-
background-color: white;
|
|
40
|
-
border-radius: 4px;
|
|
41
|
-
padding: 4px;
|
|
42
|
-
align-items: center;
|
|
43
|
-
box-shadow: 0 0 10px rgb(0 0 0 / 20%);
|
|
44
|
-
display: inline-flex;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.OpenPanelIcon-verticalGroup {
|
|
48
|
-
@extend %OpenPanelIcon-Group;
|
|
49
|
-
flex-direction: column;
|
|
50
|
-
|
|
51
|
-
.OpenPanelIcon-separator {
|
|
52
|
-
margin: 6px 0;
|
|
53
|
-
height: 2px;
|
|
54
|
-
width: 100%;
|
|
55
|
-
background-color: lui.$grey-10;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.OpenPanelIcon-horizontalGroup {
|
|
60
|
-
@extend %OpenPanelIcon-Group;
|
|
61
|
-
flex-direction: row;
|
|
62
|
-
|
|
63
|
-
.OpenPanelIcon-separator {
|
|
64
|
-
height: 100%;
|
|
65
|
-
margin-left: 6px;
|
|
66
|
-
margin-right: 6px;
|
|
67
|
-
margin-top: -3px;
|
|
68
|
-
padding-bottom: 8px;
|
|
69
|
-
width: 2px;
|
|
70
|
-
background-color: lui.$grey-10;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import './OpenPanelIcon.scss';
|
|
2
|
-
|
|
3
|
-
import { LuiIcon } from '@linzjs/lui';
|
|
4
|
-
import { IconName } from '@linzjs/lui/dist/components/LuiIcon/LuiIcon';
|
|
5
|
-
import clsx from 'clsx';
|
|
6
|
-
import { PropsWithChildren, useContext } from 'react';
|
|
7
|
-
|
|
8
|
-
import { OpenPanelOptions, PanelsContext } from './PanelsContext';
|
|
9
|
-
|
|
10
|
-
export const ButtonIconHorizontalGroup = ({ children }: PropsWithChildren) => (
|
|
11
|
-
<div>
|
|
12
|
-
<div className={'OpenPanelIcon-horizontalGroup'}>{children}</div>
|
|
13
|
-
</div>
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
export const ButtonIconVerticalGroup = ({ children }: PropsWithChildren) => (
|
|
17
|
-
<div>
|
|
18
|
-
<div className={'OpenPanelIcon-verticalGroup'}>{children}</div>
|
|
19
|
-
</div>
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
export const ButtonIconSeparator = () => <div className="OpenPanelIcon-separator"> </div>;
|
|
23
|
-
|
|
24
|
-
interface OpenPanelIconProps extends OpenPanelOptions {
|
|
25
|
-
iconTitle: string;
|
|
26
|
-
icon: IconName;
|
|
27
|
-
disabled?: boolean;
|
|
28
|
-
className?: string;
|
|
29
|
-
testId?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const OpenPanelIcon = ({
|
|
33
|
-
iconTitle,
|
|
34
|
-
icon,
|
|
35
|
-
className,
|
|
36
|
-
disabled,
|
|
37
|
-
testId,
|
|
38
|
-
uniqueId = iconTitle,
|
|
39
|
-
...openPanelOptions
|
|
40
|
-
}: OpenPanelIconProps) => {
|
|
41
|
-
const { openPanel, openPanels } = useContext(PanelsContext);
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<button
|
|
45
|
-
type="button"
|
|
46
|
-
className={clsx(
|
|
47
|
-
className,
|
|
48
|
-
'lui-button lui-button-secondary lui-button-toolbar panel-button',
|
|
49
|
-
openPanels.has(uniqueId) && 'OpenPanelIcon-selected',
|
|
50
|
-
disabled && 'OpenPanelIcon-disabled',
|
|
51
|
-
)}
|
|
52
|
-
title={iconTitle}
|
|
53
|
-
onClick={() => openPanel({ uniqueId, ...openPanelOptions })}
|
|
54
|
-
disabled={disabled}
|
|
55
|
-
data-testid={testId}
|
|
56
|
-
>
|
|
57
|
-
<LuiIcon name={icon} alt={iconTitle} size={'md'} />
|
|
58
|
-
</button>
|
|
59
|
-
);
|
|
60
|
-
};
|