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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lupine.components",
3
- "version": "1.1.15",
3
+ "version": "1.1.16",
4
4
  "license": "MIT",
5
5
  "author": "uuware.com",
6
6
  "homepage": "https://github.com/uuware/lupine.js",
@@ -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';
@@ -6,6 +6,7 @@ export type MenuItemProps = {
6
6
  alt?: string;
7
7
  hide?: boolean;
8
8
  devAdmin?: boolean;
9
+ indent?: number;
9
10
  };
10
11
 
11
12
  export type NestMenuItemProps = MenuItemProps & {
@@ -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
- ref.$('.popup-menu-item .popup-menu-text').innerText = label;
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
- <span class={'popup-menu-text' + (noTriangleIcon !== true ? ' triangle-icon' : '')}>
268
- {defaultValue || '&nbsp;'}
269
- </span>
286
+ {icon ? (
287
+ icon
288
+ ) : (
289
+ <span class={'popup-menu-text' + (noTriangleIcon !== true ? ' triangle-icon' : '')}>
290
+ {defaultValue || '&nbsp;'}
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
- return item === '' ? (
276
- <hr />
277
- ) : (
278
- <div class='item' onClick={itemClick}>
279
- {item}
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
  })}
@@ -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}>{children}</div>;
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 list={list} defaultValue={currentTheme.themeName} handleSelected={handleSelected}></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: string;
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>