lupine.components 1.1.15 → 1.1.16
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/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/menu-item-props.tsx +1 -0
- package/src/components/popup-menu.tsx +38 -14
- package/src/components/svg.tsx +9 -1
- package/src/components/switch-option-component.tsx +74 -0
- package/src/components/theme-selector.tsx +11 -3
- package/src/frames/responsive-frame.tsx +2 -2
package/package.json
CHANGED
package/src/components/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export * from './slide-tab-component';
|
|
|
32
32
|
export * from './spinner';
|
|
33
33
|
export * from './stars-component';
|
|
34
34
|
export * from './svg';
|
|
35
|
+
export * from './switch-option-component';
|
|
35
36
|
export * from './tabs';
|
|
36
37
|
export * from './text-glow';
|
|
37
38
|
export * from './text-scale';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CssProps, RefProps, VNode } from 'lupine.web';
|
|
2
2
|
import { stopPropagation } from '../lib';
|
|
3
|
+
import { MenuItemProps } from './menu-item-props';
|
|
3
4
|
|
|
4
5
|
export type PopupMenuHookProps = {
|
|
5
6
|
openMenu?: (event?: MouseEvent) => void;
|
|
@@ -43,9 +44,11 @@ export const PopupMenuWithIcon = (props: PopupMenuProps) => {
|
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
export type PopupMenuProps = {
|
|
46
|
-
list: string[];
|
|
47
|
+
list: (string | MenuItemProps)[];
|
|
47
48
|
defaultValue: string;
|
|
49
|
+
icon?: VNode<any>;
|
|
48
50
|
tips?: string;
|
|
51
|
+
width?: string;
|
|
49
52
|
minWidth?: string;
|
|
50
53
|
maxWidth?: string;
|
|
51
54
|
maxHeight?: string;
|
|
@@ -124,7 +127,9 @@ export const PopupMenuWithLabel = (props: PopupMenuWithLabelProps) => {
|
|
|
124
127
|
export const PopupMenu = ({
|
|
125
128
|
list,
|
|
126
129
|
defaultValue,
|
|
130
|
+
icon,
|
|
127
131
|
tips = '',
|
|
132
|
+
width,
|
|
128
133
|
minWidth,
|
|
129
134
|
maxWidth,
|
|
130
135
|
maxHeight,
|
|
@@ -172,7 +177,7 @@ export const PopupMenu = ({
|
|
|
172
177
|
position: 'absolute',
|
|
173
178
|
fontSize: 'var(--menu-font-size)',
|
|
174
179
|
top: 0,
|
|
175
|
-
width: '100px',
|
|
180
|
+
width: width || '100px',
|
|
176
181
|
color: 'var(--activatable-color-normal)',
|
|
177
182
|
backgroundColor: 'var(--activatable-bg-color-normal)',
|
|
178
183
|
zIndex: 'var(--layer-menu)',
|
|
@@ -198,6 +203,18 @@ export const PopupMenu = ({
|
|
|
198
203
|
backgroundColor: 'var(--activatable-bg-color-hover)',
|
|
199
204
|
cursor: 'pointer',
|
|
200
205
|
},
|
|
206
|
+
'.item.indent1': {
|
|
207
|
+
paddingLeft: '19px',
|
|
208
|
+
},
|
|
209
|
+
'.item.indent1:hover': {
|
|
210
|
+
paddingLeft: '18px',
|
|
211
|
+
},
|
|
212
|
+
'.item.indent2': {
|
|
213
|
+
paddingLeft: '35px',
|
|
214
|
+
},
|
|
215
|
+
'.item.indent2:hover': {
|
|
216
|
+
paddingLeft: '34px',
|
|
217
|
+
},
|
|
201
218
|
},
|
|
202
219
|
'.popup-menu-list.left-align': {
|
|
203
220
|
left: '2px',
|
|
@@ -233,10 +250,12 @@ export const PopupMenu = ({
|
|
|
233
250
|
hook.openMenu = openMenu;
|
|
234
251
|
hook.getValue = () => selectedValue;
|
|
235
252
|
hook.setLabel = (label: string) => {
|
|
236
|
-
|
|
253
|
+
if (!icon && noUpdateLabel !== true) {
|
|
254
|
+
ref.$('.popup-menu-item .popup-menu-text').innerText = label;
|
|
255
|
+
}
|
|
237
256
|
};
|
|
238
257
|
}
|
|
239
|
-
const itemClick = (event: any) => {
|
|
258
|
+
const itemClick = (event: any, item: any) => {
|
|
240
259
|
stopPropagation(event);
|
|
241
260
|
|
|
242
261
|
// console.log('=======', event);
|
|
@@ -244,11 +263,11 @@ export const PopupMenu = ({
|
|
|
244
263
|
ref.$('.popup-menu-list').classList.remove('open');
|
|
245
264
|
if (event.target) {
|
|
246
265
|
selectedValue = event.target.innerText;
|
|
247
|
-
if (noUpdateLabel !== true) {
|
|
266
|
+
if (!icon && noUpdateLabel !== true) {
|
|
248
267
|
ref.$('.popup-menu-item .popup-menu-text').innerText = event.target.innerText;
|
|
249
268
|
}
|
|
250
269
|
if (handleSelected) {
|
|
251
|
-
handleSelected(event.target.innerText);
|
|
270
|
+
handleSelected(event.target.innerText, item);
|
|
252
271
|
}
|
|
253
272
|
}
|
|
254
273
|
handleClosed && handleClosed();
|
|
@@ -264,19 +283,24 @@ export const PopupMenu = ({
|
|
|
264
283
|
return (
|
|
265
284
|
<div ref={ref} css={css} onClick={openMenu} title={tips}>
|
|
266
285
|
<div class='popup-menu-item'>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
286
|
+
{icon ? (
|
|
287
|
+
icon
|
|
288
|
+
) : (
|
|
289
|
+
<span class={'popup-menu-text' + (noTriangleIcon !== true ? ' triangle-icon' : '')}>
|
|
290
|
+
{defaultValue || ' '}
|
|
291
|
+
</span>
|
|
292
|
+
)}
|
|
270
293
|
</div>
|
|
271
294
|
<div class='popup-menu-bottom'>
|
|
272
295
|
<div class='popup-menu-list'>
|
|
273
296
|
<div>
|
|
274
297
|
{list.map((item) => {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
298
|
+
if (item === '') return <hr />;
|
|
299
|
+
const text = typeof item === 'string' ? item : item.text;
|
|
300
|
+
const indent = typeof item === 'string' ? 0 : item.indent;
|
|
301
|
+
return (
|
|
302
|
+
<div class={'item' + (indent ? ' indent' + indent : '')} onClick={(e) => itemClick(e, item)}>
|
|
303
|
+
{text}
|
|
280
304
|
</div>
|
|
281
305
|
);
|
|
282
306
|
})}
|
package/src/components/svg.tsx
CHANGED
|
@@ -11,6 +11,14 @@ export const Svg = ({
|
|
|
11
11
|
height?: string;
|
|
12
12
|
color?: string;
|
|
13
13
|
}) => {
|
|
14
|
+
let content = children || '';
|
|
15
|
+
if (content.startsWith('data:image/svg+xml,')) {
|
|
16
|
+
content = decodeURIComponent(content.slice('data:image/svg+xml,'.length));
|
|
17
|
+
} else if (content.includes('%') && content.includes('<svg')) {
|
|
18
|
+
// Handle cases where it's encoded but prefix is missing
|
|
19
|
+
content = decodeURIComponent(content);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
const css: any = {
|
|
15
23
|
svg: {
|
|
16
24
|
maxWidth: '100%',
|
|
@@ -20,5 +28,5 @@ export const Svg = ({
|
|
|
20
28
|
fill: color,
|
|
21
29
|
},
|
|
22
30
|
};
|
|
23
|
-
return <div css={css}
|
|
31
|
+
return <div css={css} dangerouslySetInnerHTML={content}></div>;
|
|
24
32
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { bindGlobalStyle, CssProps, RefProps } from 'lupine.components';
|
|
2
|
+
|
|
3
|
+
export type SwitchOptionHookComponentProps = {
|
|
4
|
+
setValue?: (value: string) => void;
|
|
5
|
+
getValue?: () => string;
|
|
6
|
+
};
|
|
7
|
+
export type SwitchOptionComponentProps = {
|
|
8
|
+
option1: string;
|
|
9
|
+
option2: string;
|
|
10
|
+
defaultOption: string;
|
|
11
|
+
onChange?: (value: string) => void;
|
|
12
|
+
hook?: SwitchOptionHookComponentProps;
|
|
13
|
+
fontSize?: string;
|
|
14
|
+
};
|
|
15
|
+
export const SwitchOptionComponent = (props: SwitchOptionComponentProps) => {
|
|
16
|
+
const css: CssProps = {
|
|
17
|
+
display: 'flex',
|
|
18
|
+
flexDirection: 'row',
|
|
19
|
+
borderRadius: '9999px',
|
|
20
|
+
padding: '2px 4px',
|
|
21
|
+
fontSize: '0.7rem',
|
|
22
|
+
backgroundColor: '#e7e7e7',
|
|
23
|
+
width: 'fit-content',
|
|
24
|
+
'.switch-btn': {
|
|
25
|
+
padding: '4px',
|
|
26
|
+
borderRadius: '50%',
|
|
27
|
+
border: 'none',
|
|
28
|
+
background: 'transparent',
|
|
29
|
+
color: 'inherit',
|
|
30
|
+
cursor: 'pointer',
|
|
31
|
+
transition: 'all 0.2s',
|
|
32
|
+
},
|
|
33
|
+
'.switch-btn:first-child': {
|
|
34
|
+
marginRight: '4px',
|
|
35
|
+
},
|
|
36
|
+
'.switch-btn.active': {
|
|
37
|
+
backgroundColor: '#fff',
|
|
38
|
+
color: '#000000',
|
|
39
|
+
boxShadow: '2px 1px 2px 1px rgb(189 189 189)',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
bindGlobalStyle('switch-option-box', css);
|
|
43
|
+
|
|
44
|
+
const onNotationChange = (value: string) => {
|
|
45
|
+
props.defaultOption = value;
|
|
46
|
+
const btns = ref.$all('.switch-btn') as NodeListOf<Element>;
|
|
47
|
+
btns[0].classList.toggle('active', value === props.option1);
|
|
48
|
+
btns[1].classList.toggle('active', value === props.option2);
|
|
49
|
+
props.onChange?.(value);
|
|
50
|
+
};
|
|
51
|
+
if (props.hook) {
|
|
52
|
+
props.hook.setValue = (value: string) => {
|
|
53
|
+
onNotationChange(value);
|
|
54
|
+
};
|
|
55
|
+
props.hook.getValue = () => props.defaultOption;
|
|
56
|
+
}
|
|
57
|
+
const ref: RefProps = {};
|
|
58
|
+
return (
|
|
59
|
+
<div style={{ fontSize: props.fontSize }} ref={ref} class='switch-option-box'>
|
|
60
|
+
<button
|
|
61
|
+
onClick={() => onNotationChange(props.option1)}
|
|
62
|
+
className={`switch-btn ${props.defaultOption === props.option1 ? 'active' : ''}`}
|
|
63
|
+
>
|
|
64
|
+
{props.option1}
|
|
65
|
+
</button>
|
|
66
|
+
<button
|
|
67
|
+
onClick={() => onNotationChange(props.option2)}
|
|
68
|
+
className={`switch-btn ${props.defaultOption === props.option2 ? 'active' : ''}`}
|
|
69
|
+
>
|
|
70
|
+
{props.option2}
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { CssProps, getCurrentTheme, updateTheme } from 'lupine.web';
|
|
1
|
+
import { CssProps, getCurrentTheme, updateTheme, VNode } from 'lupine.web';
|
|
2
2
|
import { PopupMenu } from './popup-menu';
|
|
3
3
|
|
|
4
4
|
export type ThemeSelectorProps = {
|
|
5
5
|
className?: string;
|
|
6
|
+
icon?: VNode<any>;
|
|
7
|
+
noUpdateLabel?: boolean;
|
|
6
8
|
};
|
|
7
9
|
|
|
8
|
-
export const ThemeSelector = ({ className }: ThemeSelectorProps) => {
|
|
10
|
+
export const ThemeSelector = ({ className, icon, noUpdateLabel }: ThemeSelectorProps) => {
|
|
9
11
|
const css: CssProps = {
|
|
10
12
|
display: 'flex',
|
|
11
13
|
flexDirection: 'column',
|
|
@@ -22,7 +24,13 @@ export const ThemeSelector = ({ className }: ThemeSelectorProps) => {
|
|
|
22
24
|
}
|
|
23
25
|
return (
|
|
24
26
|
<div css={css} class={['theme-switch', className].join(' ')} title='Select theme'>
|
|
25
|
-
<PopupMenu
|
|
27
|
+
<PopupMenu
|
|
28
|
+
list={list}
|
|
29
|
+
defaultValue={currentTheme.themeName}
|
|
30
|
+
handleSelected={handleSelected}
|
|
31
|
+
icon={icon}
|
|
32
|
+
noUpdateLabel={noUpdateLabel}
|
|
33
|
+
></PopupMenu>
|
|
26
34
|
</div>
|
|
27
35
|
);
|
|
28
36
|
};
|
|
@@ -14,7 +14,7 @@ export interface ResponsiveFrameProps {
|
|
|
14
14
|
placeholderClassname: string;
|
|
15
15
|
mainContent: VNode<any>;
|
|
16
16
|
desktopHeaderTitle: string;
|
|
17
|
-
desktopFooterTitle
|
|
17
|
+
desktopFooterTitle?: string;
|
|
18
18
|
desktopTopMenu: IconMenuItemProps[];
|
|
19
19
|
mobileBottomMenu: IconMenuItemProps[];
|
|
20
20
|
mobileSideMenuContent: VNode<any>;
|
|
@@ -74,7 +74,7 @@ export const ResponsiveFrame = async (props: ResponsiveFrameProps) => {
|
|
|
74
74
|
<MobileSideMenu>{props.mobileSideMenuContent}</MobileSideMenu>
|
|
75
75
|
<div class={'content-block ' + props.placeholderClassname}>{props.mainContent}</div>
|
|
76
76
|
<div class='frame-footer'>
|
|
77
|
-
<DesktopFooter title={props.desktopFooterTitle}></DesktopFooter>
|
|
77
|
+
{props.desktopFooterTitle && <DesktopFooter title={props.desktopFooterTitle}></DesktopFooter>}
|
|
78
78
|
<MobileFooterMenu items={props.mobileBottomMenu}></MobileFooterMenu>
|
|
79
79
|
</div>
|
|
80
80
|
</div>
|