lupine.components 1.1.21 → 1.1.22

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.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/src/components/action-sheet-demo.tsx +87 -0
  3. package/src/components/button-demo.tsx +24 -0
  4. package/src/components/button-push-animation-demo.tsx +24 -0
  5. package/src/components/button-push-animation.tsx +8 -4
  6. package/src/components/editable-label-demo.tsx +25 -0
  7. package/src/components/input-with-title-demo.tsx +31 -0
  8. package/src/components/message-box-demo.tsx +80 -0
  9. package/src/components/modal-demo.tsx +44 -0
  10. package/src/components/notice-message-demo.tsx +52 -0
  11. package/src/components/popup-menu-demo.tsx +49 -0
  12. package/src/components/popup-menu.tsx +1 -1
  13. package/src/components/progress-demo.tsx +51 -0
  14. package/src/components/progress.tsx +3 -3
  15. package/src/components/radio-label-demo.tsx +27 -0
  16. package/src/components/redirect-demo.tsx +28 -0
  17. package/src/components/resizable-splitter-demo.tsx +68 -0
  18. package/src/components/resizable-splitter.tsx +6 -6
  19. package/src/components/select-angle-demo.tsx +24 -0
  20. package/src/components/select-with-title-demo.tsx +36 -0
  21. package/src/components/spinner-demo.tsx +41 -0
  22. package/src/components/stars-component-demo.tsx +26 -0
  23. package/src/components/stars-component.tsx +11 -3
  24. package/src/components/switch-option-demo.tsx +26 -0
  25. package/src/components/tabs-demo.tsx +30 -0
  26. package/src/components/text-glow-demo.tsx +36 -0
  27. package/src/components/text-scale-demo.tsx +30 -0
  28. package/src/components/text-wave-demo.tsx +36 -0
  29. package/src/components/toggle-button-demo.tsx +32 -0
  30. package/src/components/toggle-play-button-demo.tsx +44 -0
  31. package/src/components/toggle-switch-demo.tsx +33 -0
  32. package/src/demo/demo-about.tsx +12 -0
  33. package/src/demo/demo-container.tsx +57 -0
  34. package/src/demo/demo-css.tsx +3 -0
  35. package/src/demo/demo-frame-helper.tsx +395 -0
  36. package/src/demo/demo-frame.tsx +139 -0
  37. package/src/demo/demo-index.tsx +14 -0
  38. package/src/demo/demo-page.tsx +138 -0
  39. package/src/demo/demo-registry.ts +54 -0
  40. package/src/demo/demo-render-page.tsx +54 -0
  41. package/src/demo/demo-types.ts +21 -0
  42. package/src/html-editor/buttons_morden.gif +0 -0
  43. package/src/html-editor/h-editor.ts +817 -0
  44. package/src/index.ts +2 -0
@@ -0,0 +1,68 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { ResizableSplitter } from './resizable-splitter';
3
+
4
+ export const resizableSplitterDemo: DemoStory<any> = {
5
+ id: 'resizable-splitter-demo',
6
+ text: 'Resizable Splitter Demo',
7
+ args: {
8
+ isVertical: true,
9
+ },
10
+ argTypes: {
11
+ isVertical: { control: 'boolean', description: 'If true, splits left/right. If false, splits top/bottom' },
12
+ },
13
+ render: (args) => {
14
+ // The splitter hook targets a selector, so we create a unique container.
15
+ const containerId = 'splitter-demo-box';
16
+
17
+ // The logic expects to re-mount the splitter or reload the page for structural updates normally,
18
+ // but we can hack it together for the demo by destroying and recreating the DOM.
19
+ // For simplicity, we just use a fixed layout that reacts to the boolean toggle.
20
+
21
+ if (args.isVertical) {
22
+ return (
23
+ <div style={{ display: 'flex', width: '600px', height: '400px', border: '1px solid #999', padding: '20px' }}>
24
+ <div
25
+ id={containerId}
26
+ style={{
27
+ width: '200px',
28
+ height: '100%',
29
+ backgroundColor: '#eee',
30
+ position: 'relative',
31
+ }}
32
+ >
33
+ <div style={{ padding: '10px' }}>Left Panel (Drag right edge)</div>
34
+ {ResizableSplitter.getSplitter(`#${containerId}`, true, true)}
35
+ </div>
36
+ <div style={{ flex: 1, backgroundColor: '#ddd', padding: '10px' }}>Right Panel (Flex 1)</div>
37
+ </div>
38
+ );
39
+ } else {
40
+ return (
41
+ <div
42
+ style={{
43
+ display: 'flex',
44
+ flexDirection: 'column',
45
+ width: '600px',
46
+ height: '400px',
47
+ border: '1px solid #999',
48
+ padding: '20px',
49
+ }}
50
+ >
51
+ <div
52
+ id={containerId}
53
+ style={{
54
+ height: '150px',
55
+ width: '100%',
56
+ backgroundColor: '#eee',
57
+ position: 'relative',
58
+ }}
59
+ >
60
+ <div style={{ padding: '10px' }}>Top Panel (Drag bottom edge)</div>
61
+ {ResizableSplitter.getSplitter(`#${containerId}`, false, false)}
62
+ </div>
63
+ <div style={{ flex: 1, backgroundColor: '#ddd', padding: '10px' }}>Bottom Panel (Flex 1)</div>
64
+ </div>
65
+ );
66
+ }
67
+ },
68
+ };
@@ -63,7 +63,7 @@ export class ResizableSplitter {
63
63
  backgroundColor: '#ccc',
64
64
  },
65
65
  };
66
- bindGlobalStyle('resizable-splitter', css);
66
+ bindGlobalStyle('resizable-splitter', css, false, true);
67
67
 
68
68
  window.addEventListener('mousemove', ResizableSplitter.onMousemove.bind(ResizableSplitter), false);
69
69
  document.documentElement.addEventListener('mouseup', ResizableSplitter.onMouseup.bind(ResizableSplitter), false);
@@ -105,14 +105,14 @@ export class ResizableSplitter {
105
105
  }
106
106
 
107
107
  static getSplitter(selector: string, isVertical: boolean, isRightOrTop: boolean) {
108
+ if (!this.initialized) {
109
+ this.initialized = true;
110
+ this.init();
111
+ }
112
+
108
113
  const className = this.getSplitterClassName(isVertical, isRightOrTop);
109
114
 
110
115
  const onMousedown = (event: any) => {
111
- if (!this.initialized) {
112
- this.initialized = true;
113
- this.init();
114
- }
115
-
116
116
  ResizableSplitter.hostNode = document.querySelector(selector)!;
117
117
  if (!ResizableSplitter.hostNode) {
118
118
  console.error(`Can't find element: ${selector}`);
@@ -0,0 +1,24 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { SelectAngleComponent, SelectAngleComponentProps } from './select-angle-component';
3
+
4
+ export const selectAngleDemo: DemoStory<SelectAngleComponentProps> = {
5
+ id: 'select-angle-demo',
6
+ text: 'Select Angle Demo',
7
+ args: {
8
+ size: '100px',
9
+ angle: 45,
10
+ onChange: () => {},
11
+ },
12
+ argTypes: {
13
+ size: { control: 'text', description: 'Size of the dial container' },
14
+ angle: { control: 'number', description: 'Initial angle (0-360)' },
15
+ },
16
+ render: (args) => {
17
+ return (
18
+ <div style={{ padding: '20px' }}>
19
+ <p style={{ color: '#666', marginBottom: '16px' }}>Drag the needle or click the edge dots to set the angle.</p>
20
+ <SelectAngleComponent {...args} onChange={(val) => console.log('Angle changed:', val)} />
21
+ </div>
22
+ );
23
+ },
24
+ };
@@ -0,0 +1,36 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { SelectWithTitle, SelectOptionProps } from './select-with-title';
3
+
4
+ export const selectWithTitleDemo: DemoStory<any> = {
5
+ id: 'select-with-title-demo',
6
+ text: 'Select With Title Demo',
7
+ args: {
8
+ title: 'Choose a fruit',
9
+ size: 1,
10
+ width: '200px',
11
+ },
12
+ argTypes: {
13
+ title: { control: 'text', description: 'Title text shown above' },
14
+ size: { control: 'number', description: 'HTML select size attribute' },
15
+ width: { control: 'text', description: 'Width of the select' },
16
+ },
17
+ render: (args) => {
18
+ const options: SelectOptionProps[] = [
19
+ { option: 'Apple', value: 'apple' },
20
+ { option: 'Banana', value: 'banana', selected: true },
21
+ { option: 'Cherry', value: 'cherry' },
22
+ ];
23
+ return (
24
+ <div style={{ padding: '20px' }}>
25
+ {SelectWithTitle(
26
+ args.title,
27
+ options,
28
+ (val) => console.log('Selected:', val),
29
+ args.size,
30
+ 'input-base',
31
+ args.width
32
+ )}
33
+ </div>
34
+ );
35
+ },
36
+ };
@@ -0,0 +1,41 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { Spinner01, Spinner02, Spinner03, SpinnerSize } from './spinner';
3
+
4
+ export const spinnerDemo: DemoStory<any> = {
5
+ id: 'spinner-demo',
6
+ text: 'Spinner Demo',
7
+ args: {
8
+ size: SpinnerSize.Medium,
9
+ color: '#0a74c9',
10
+ },
11
+ argTypes: {
12
+ size: {
13
+ control: 'select',
14
+ options: Object.values(SpinnerSize),
15
+ description: 'Size of the spinner',
16
+ },
17
+ color: {
18
+ control: 'text',
19
+ description: 'Color of the spinner (Hex, RGB, or var)',
20
+ },
21
+ },
22
+ render: (args) => {
23
+ return (
24
+ <div style={{ display: 'flex', gap: '40px', alignItems: 'center', padding: '20px' }}>
25
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }}>
26
+ <Spinner01 size={args.size} color={args.color} />
27
+ <span>Spinner01</span>
28
+ </div>
29
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }}>
30
+ <Spinner02 size={args.size} color={args.color} />
31
+ <span>Spinner02</span>
32
+ </div>
33
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }}>
34
+ {/* Spinner03 expects an RGB tuple string without the rgb() wrapper */}
35
+ <Spinner03 size={args.size} colorRGB='10 116 201' />
36
+ <span>Spinner03</span>
37
+ </div>
38
+ </div>
39
+ );
40
+ },
41
+ };
@@ -0,0 +1,26 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { StarsComponent, StarsComponentProps } from './stars-component';
3
+
4
+ export const starsDemo: DemoStory<StarsComponentProps> = {
5
+ id: 'stars-demo',
6
+ text: 'Stars Component Demo',
7
+ args: {
8
+ maxLength: 5,
9
+ value: 3,
10
+ fontSize: '30px',
11
+ fullIcon: <div class='ifc-icon full'>★</div>,
12
+ outlineIcon: <div class='ifc-icon outline'>☆</div>,
13
+ },
14
+ argTypes: {
15
+ maxLength: { control: 'number', description: 'Total number of stars' },
16
+ value: { control: 'number', description: 'Current rating value' },
17
+ fontSize: { control: 'text', description: 'Size of the stars (e.g., 20px, 2rem)' },
18
+ },
19
+ render: (args: StarsComponentProps) => {
20
+ return (
21
+ <div style={{ padding: '20px' }}>
22
+ <StarsComponent {...args} />
23
+ </div>
24
+ );
25
+ },
26
+ };
@@ -1,5 +1,8 @@
1
- import { bindGlobalStyle, CssProps, RefProps } from 'lupine.components';
1
+ import { bindGlobalStyle, CssProps, RefProps, VNode } from 'lupine.components';
2
2
 
3
+ /*
4
+ ma-cards-heart and ma-cards-heart-outline are font icons
5
+ */
3
6
  export type StarsHookComponentProps = {
4
7
  setValue: (value: number) => void;
5
8
  getValue: () => number;
@@ -10,6 +13,8 @@ export type StarsComponentProps = {
10
13
  onChange?: (value: number) => void;
11
14
  hook?: StarsHookComponentProps;
12
15
  fontSize?: string;
16
+ fullIcon?: VNode<any>; // should contain full classname
17
+ outlineIcon?: VNode<any>; // should contain outline classname
13
18
  };
14
19
  export const StarsComponent = (props: StarsComponentProps) => {
15
20
  const css: CssProps = {
@@ -46,6 +51,9 @@ export const StarsComponent = (props: StarsComponentProps) => {
46
51
  };
47
52
  props.hook.getValue = () => props.value;
48
53
  }
54
+
55
+ const fullIcon = props.fullIcon || <i class='ifc-icon ma-cards-heart full'></i>;
56
+ const outlineIcon = props.outlineIcon || <i class='ifc-icon ma-cards-heart-outline outline'></i>;
49
57
  const ref: RefProps = {};
50
58
  return (
51
59
  <div style={{ fontSize: props.fontSize || '20px' }} ref={ref} class='stars-box'>
@@ -57,8 +65,8 @@ export const StarsComponent = (props: StarsComponentProps) => {
57
65
  props.onChange?.(index + 1);
58
66
  }}
59
67
  >
60
- <i class='ifc-icon ma-cards-heart full'></i>
61
- <i class='ifc-icon ma-cards-heart-outline outline'></i>
68
+ {fullIcon}
69
+ {outlineIcon}
62
70
  </label>
63
71
  ))}
64
72
  </div>
@@ -0,0 +1,26 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { SwitchOptionComponent, SwitchOptionComponentProps } from './switch-option-component';
3
+
4
+ export const switchOptionDemo: DemoStory<SwitchOptionComponentProps> = {
5
+ id: 'switch-option-demo',
6
+ text: 'Switch Option Demo',
7
+ args: {
8
+ option1: 'Day',
9
+ option2: 'Night',
10
+ defaultOption: 'Day',
11
+ fontSize: '14px',
12
+ },
13
+ argTypes: {
14
+ option1: { control: 'text', description: 'First option text' },
15
+ option2: { control: 'text', description: 'Second option text' },
16
+ defaultOption: { control: 'text', description: 'Initial selected option' },
17
+ fontSize: { control: 'text', description: 'Font size' },
18
+ },
19
+ render: (args) => {
20
+ return (
21
+ <div style={{ padding: '20px' }}>
22
+ <SwitchOptionComponent {...args} onChange={(val) => console.log('Switched to:', val)} />
23
+ </div>
24
+ );
25
+ },
26
+ };
@@ -0,0 +1,30 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { Tabs } from './tabs';
3
+
4
+ export const tabsDemo: DemoStory<any> = {
5
+ id: 'tabs-demo',
6
+ text: 'Tabs Demo',
7
+ args: {
8
+ defaultIndex: 0,
9
+ pagePadding: '16px',
10
+ },
11
+ argTypes: {
12
+ defaultIndex: { control: 'number', description: 'Index of the active tab on load' },
13
+ pagePadding: { control: 'text', description: 'Padding inside each tab page' },
14
+ },
15
+ render: (args) => {
16
+ return (
17
+ <div style={{ height: '300px', width: '500px', border: '1px solid #ccc' }}>
18
+ <Tabs
19
+ defaultIndex={args.defaultIndex}
20
+ pagePadding={args.pagePadding}
21
+ pages={[
22
+ { title: 'Tab 1', page: <div>Content for Tab 1</div> },
23
+ { title: 'Tab 2', page: <div>Content for Tab 2</div> },
24
+ { title: 'Tab 3', page: <div style={{ color: 'red' }}>Content for Tab 3</div> },
25
+ ]}
26
+ />
27
+ </div>
28
+ );
29
+ },
30
+ };
@@ -0,0 +1,36 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { TextGlow, TextGlowProps } from './text-glow';
3
+
4
+ export const textGlowDemo: DemoStory<TextGlowProps> = {
5
+ id: 'text-glow-demo',
6
+ text: 'Text Glow Demo',
7
+ args: {
8
+ text: 'NEON GLOW',
9
+ color: '#ffffff',
10
+ fontSize: '40px',
11
+ padding: '30px',
12
+ fontWeight: 'bold',
13
+ },
14
+ argTypes: {
15
+ text: { control: 'text', description: 'The text to animate' },
16
+ color: { control: 'text', description: 'Color of the base text' },
17
+ fontSize: { control: 'text', description: 'Font size' },
18
+ padding: { control: 'text', description: 'Padding around text' },
19
+ fontWeight: { control: 'text', description: 'Font weight' },
20
+ },
21
+ render: (args) => {
22
+ return (
23
+ <div
24
+ style={{
25
+ display: 'flex',
26
+ justifyContent: 'center',
27
+ alignItems: 'center',
28
+ height: '200px',
29
+ backgroundColor: '#111',
30
+ }}
31
+ >
32
+ <TextGlow {...args} />
33
+ </div>
34
+ );
35
+ },
36
+ };
@@ -0,0 +1,30 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { TextScale, TextScaleProps } from './text-scale';
3
+
4
+ export const textScaleDemo: DemoStory<TextScaleProps> = {
5
+ id: 'text-scale-demo',
6
+ text: 'Text Scale Demo',
7
+ args: {
8
+ text: 'Scaling Text!!!',
9
+ color: '#22b8ff',
10
+ backgroundColor: '#a1ffe8',
11
+ fontSize: '35px',
12
+ padding: '15px 30px',
13
+ fontWeight: 'bold',
14
+ },
15
+ argTypes: {
16
+ text: { control: 'text', description: 'The text to animate' },
17
+ color: { control: 'text', description: 'Color of the text' },
18
+ backgroundColor: { control: 'text', description: 'Background color behind text' },
19
+ fontSize: { control: 'text', description: 'Font size' },
20
+ padding: { control: 'text', description: 'Padding around background' },
21
+ fontWeight: { control: 'text', description: 'Font weight' },
22
+ },
23
+ render: (args) => {
24
+ return (
25
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '200px' }}>
26
+ <TextScale {...args} />
27
+ </div>
28
+ );
29
+ },
30
+ };
@@ -0,0 +1,36 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { TextWave, TextLoadingProps } from './text-wave';
3
+
4
+ export const textWaveDemo: DemoStory<TextLoadingProps> = {
5
+ id: 'text-wave-demo',
6
+ text: 'Text Wave Demo',
7
+ args: {
8
+ text: 'Loading...',
9
+ color: '#22b8ff',
10
+ fontSize: '30px',
11
+ padding: '20px',
12
+ fontWeight: 'bold',
13
+ },
14
+ argTypes: {
15
+ text: { control: 'text', description: 'The text to animate' },
16
+ color: { control: 'text', description: 'Color of the text' },
17
+ fontSize: { control: 'text', description: 'Font size' },
18
+ padding: { control: 'text', description: 'Padding around text' },
19
+ fontWeight: { control: 'text', description: 'Font weight' },
20
+ },
21
+ render: (args) => {
22
+ return (
23
+ <div
24
+ style={{
25
+ display: 'flex',
26
+ justifyContent: 'center',
27
+ alignItems: 'center',
28
+ height: '200px',
29
+ backgroundColor: '#f0f0f0',
30
+ }}
31
+ >
32
+ <TextWave {...args} />
33
+ </div>
34
+ );
35
+ },
36
+ };
@@ -0,0 +1,32 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { ToggleButton } from './toggle-base';
3
+
4
+ export const toggleButtonDemo: DemoStory<any> = {
5
+ id: 'toggle-button-demo',
6
+ text: 'Toggle Button Demo',
7
+ args: {
8
+ onText: 'Turn Off',
9
+ offText: 'Turn On',
10
+ disabled: false,
11
+ checked: false,
12
+ },
13
+ argTypes: {
14
+ onText: { control: 'text', description: 'Text shown when ON' },
15
+ offText: { control: 'text', description: 'Text shown when OFF' },
16
+ disabled: { control: 'boolean', description: 'Disabled state' },
17
+ checked: { control: 'boolean', description: 'Initial state' },
18
+ },
19
+ render: (args) => {
20
+ return (
21
+ <div style={{ padding: '20px' }}>
22
+ <ToggleButton
23
+ onText={args.onText}
24
+ offText={args.offText}
25
+ disabled={args.disabled}
26
+ checked={args.checked}
27
+ onClick={(val) => console.log('Toggled:', val)}
28
+ />
29
+ </div>
30
+ );
31
+ },
32
+ };
@@ -0,0 +1,44 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { TogglePlayButton, TogglePlayButtonSize } from './toggle-base';
3
+
4
+ export const togglePlayButtonDemo: DemoStory<any> = {
5
+ id: 'toggle-play-button-demo',
6
+ text: 'Toggle Play Button Demo',
7
+ args: {
8
+ size: 'Medium',
9
+ disabled: false,
10
+ checked: false,
11
+ textColor: '#ffffff',
12
+ backgroundColor: '#3b29cc',
13
+ noWave: false,
14
+ },
15
+ argTypes: {
16
+ size: { control: 'select', options: ['Small', 'Medium', 'Large'], description: 'Size preset' },
17
+ disabled: { control: 'boolean', description: 'Disabled state' },
18
+ checked: { control: 'boolean', description: 'Is playing?' },
19
+ textColor: { control: 'text', description: 'Color of the play icon' },
20
+ backgroundColor: { control: 'text', description: 'Background color of the button' },
21
+ noWave: { control: 'boolean', description: 'Disable the background wave animation' },
22
+ },
23
+ render: (args) => {
24
+ // Map string enum back to object
25
+ const sizeMap: any = {
26
+ Small: TogglePlayButtonSize.Small,
27
+ Medium: TogglePlayButtonSize.Medium,
28
+ Large: TogglePlayButtonSize.Large,
29
+ };
30
+ return (
31
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '200px' }}>
32
+ <TogglePlayButton
33
+ size={sizeMap[args.size]}
34
+ disabled={args.disabled}
35
+ checked={args.checked}
36
+ textColor={args.textColor}
37
+ backgroundColor={args.backgroundColor}
38
+ noWave={args.noWave}
39
+ onClick={(val) => console.log('Playing:', val)}
40
+ />
41
+ </div>
42
+ );
43
+ },
44
+ };
@@ -0,0 +1,33 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { ToggleSwitch, ToggleSwitchProps, ToggleSwitchSize } from './toggle-switch';
3
+
4
+ export const toggleSwitchDemo: DemoStory<ToggleSwitchProps> = {
5
+ id: 'toggle-switch-demo',
6
+ text: 'Toggle Switch Demo',
7
+ args: {
8
+ size: ToggleSwitchSize.Medium,
9
+ disabled: false,
10
+ checked: false,
11
+ text: { on: 'ON', off: 'OFF' },
12
+ textWidth: '30px',
13
+ },
14
+ argTypes: {
15
+ size: {
16
+ control: 'select',
17
+ options: Object.values(ToggleSwitchSize),
18
+ description: 'The size of the switch',
19
+ },
20
+ disabled: { control: 'boolean', description: 'Whether the switch is disabled' },
21
+ checked: { control: 'boolean', description: 'Whether the switch is turned on' },
22
+ textWidth: { control: 'text', description: 'Fixed width for the text to prevent jumping' },
23
+ },
24
+ render: (args: ToggleSwitchProps) => {
25
+ // Note: We don't expose 'text' as a simple control because our current demo system
26
+ // handles primitives. But the user can see how it works with the default args.
27
+ return (
28
+ <div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
29
+ <ToggleSwitch {...args} />
30
+ </div>
31
+ );
32
+ },
33
+ };
@@ -0,0 +1,12 @@
1
+ import { CssProps } from 'lupine.components';
2
+
3
+ export const DemoAboutPage = () => {
4
+ const css: CssProps = {};
5
+
6
+ return (
7
+ <div css={css} class='demo-about-top'>
8
+ <div class='row-box'>This is a demo page for testing components.</div>
9
+ <div class='row-box'></div>
10
+ </div>
11
+ );
12
+ };
@@ -0,0 +1,57 @@
1
+ import { CssProps, RefProps, VNode } from 'lupine.components';
2
+
3
+ export type DemoContainerProps = {
4
+ demoUrl: string;
5
+ onIframeLoad?: (iframeWindow: Window) => void;
6
+ controlBox: VNode<any>;
7
+ };
8
+ export const DemoContainer = (props: DemoContainerProps) => {
9
+ const css: CssProps = {
10
+ height: '100%',
11
+ width: '100%',
12
+ display: 'flex',
13
+ flexDirection: 'column',
14
+ '.&-iframe-box': {
15
+ flex: 1,
16
+ minHeight: '200px', // Ensure it has some height
17
+ },
18
+ '.&-iframe': {
19
+ width: '100%',
20
+ height: '100%',
21
+ // backgroundColor: 'white', // Default background for preview
22
+ border: 'dotted 1px red',
23
+ },
24
+ '.&-control-box': {
25
+ // Allow it to grow if needed, or scroll
26
+ minHeight: '50px',
27
+ maxHeight: '50%',
28
+ overflowY: 'auto',
29
+ borderTop: '1px solid var(--border-color, #ccc)',
30
+ padding: 'var(--space-m, 8px)',
31
+ },
32
+ };
33
+
34
+ const findIframe = () => {
35
+ const iframe = ref.$('&-iframe') as HTMLIFrameElement;
36
+ return iframe;
37
+ };
38
+
39
+ const onLoad = () => {
40
+ if (props.onIframeLoad) {
41
+ const iframe = findIframe();
42
+ if (iframe.contentWindow) {
43
+ props.onIframeLoad(iframe.contentWindow);
44
+ }
45
+ }
46
+ };
47
+
48
+ const ref: RefProps = {};
49
+ return (
50
+ <div css={css} ref={ref} class='demo-container-top'>
51
+ <div class='&-iframe-box'>
52
+ <iframe class='&-iframe' src={props.demoUrl} frameBorder='0' onLoad={onLoad}></iframe>
53
+ </div>
54
+ <div class='&-control-box'>{props.controlBox}</div>
55
+ </div>
56
+ );
57
+ };
@@ -0,0 +1,3 @@
1
+ import { CssProps } from 'lupine.components';
2
+
3
+ export const demoCss: CssProps = {};