le-kit 0.1.5 → 0.1.7
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/le-kit/assets/custom-elements.json → custom-elements.json} +833 -833
- package/dist/cjs/index-D7B9TPh8.js +1835 -0
- package/dist/cjs/index-D7B9TPh8.js.map +1 -0
- package/dist/cjs/index.cjs.js +119 -0
- package/dist/cjs/index.cjs.js.map +1 -0
- package/dist/cjs/le-box.cjs.entry.js +184 -0
- package/dist/cjs/le-box.entry.cjs.js.map +1 -0
- package/dist/cjs/le-button.le-checkbox.le-component.le-popover.le-slot.le-string-input.entry.cjs.js.map +1 -0
- package/dist/cjs/le-button_6.cjs.entry.js +1200 -0
- package/dist/cjs/le-card.cjs.entry.js +29 -0
- package/dist/cjs/le-card.entry.cjs.js.map +1 -0
- package/dist/cjs/le-kit.cjs.js +25 -0
- package/dist/cjs/le-kit.cjs.js.map +1 -0
- package/dist/cjs/le-number-input.cjs.entry.js +202 -0
- package/dist/cjs/le-number-input.entry.cjs.js.map +1 -0
- package/dist/cjs/le-popup.cjs.entry.js +212 -0
- package/dist/cjs/le-popup.entry.cjs.js.map +1 -0
- package/dist/cjs/le-round-progress.cjs.entry.js +106 -0
- package/dist/cjs/le-round-progress.entry.cjs.js.map +1 -0
- package/dist/cjs/le-stack.cjs.entry.js +135 -0
- package/dist/cjs/le-stack.entry.cjs.js.map +1 -0
- package/dist/cjs/le-text.cjs.entry.js +335 -0
- package/dist/cjs/le-text.entry.cjs.js.map +1 -0
- package/dist/cjs/le-turntable.cjs.entry.js +139 -0
- package/dist/cjs/le-turntable.entry.cjs.js.map +1 -0
- package/dist/cjs/loader.cjs.js +13 -0
- package/dist/cjs/loader.cjs.js.map +1 -0
- package/dist/cjs/utils-DrsoID-a.js +152 -0
- package/dist/cjs/utils-DrsoID-a.js.map +1 -0
- package/dist/collection/collection-manifest.json +26 -0
- package/dist/collection/components/le-box/le-box.default.css +37 -0
- package/dist/collection/components/le-box/le-box.js +614 -0
- package/dist/collection/components/le-box/le-box.js.map +1 -0
- package/dist/collection/components/le-button/le-button.default.css +263 -0
- package/dist/collection/components/le-button/le-button.js +368 -0
- package/dist/collection/components/le-button/le-button.js.map +1 -0
- package/dist/collection/components/le-card/le-card.default.css +74 -0
- package/dist/collection/components/le-card/le-card.js +102 -0
- package/dist/collection/components/le-card/le-card.js.map +1 -0
- package/dist/collection/components/le-checkbox/le-checkbox.css +93 -0
- package/dist/collection/components/le-checkbox/le-checkbox.js +192 -0
- package/dist/collection/components/le-checkbox/le-checkbox.js.map +1 -0
- package/dist/collection/components/le-component/le-component.css +189 -0
- package/dist/{le-kit/le-component.entry.js → collection/components/le-component/le-component.js} +141 -24
- package/dist/collection/components/le-component/le-component.js.map +1 -0
- package/dist/collection/components/le-number-input/le-number-input.css +135 -0
- package/dist/collection/components/le-number-input/le-number-input.js +515 -0
- package/dist/collection/components/le-number-input/le-number-input.js.map +1 -0
- package/dist/collection/components/le-popover/le-popover.css +143 -0
- package/dist/collection/components/le-popover/le-popover.js +693 -0
- package/dist/collection/components/le-popover/le-popover.js.map +1 -0
- package/dist/collection/components/le-popup/le-popup.api.js +101 -0
- package/dist/collection/components/le-popup/le-popup.api.js.map +1 -0
- package/dist/collection/components/le-popup/le-popup.css +222 -0
- package/dist/collection/components/le-popup/le-popup.js +596 -0
- package/dist/collection/components/le-popup/le-popup.js.map +1 -0
- package/dist/collection/components/le-round-progress/le-round-progress.css +34 -0
- package/dist/collection/components/le-round-progress/le-round-progress.js +184 -0
- package/dist/collection/components/le-round-progress/le-round-progress.js.map +1 -0
- package/dist/collection/components/le-slot/le-slot.default.css +222 -0
- package/dist/{le-kit/le-slot.entry.js → collection/components/le-slot/le-slot.js} +270 -20
- package/dist/collection/components/le-slot/le-slot.js.map +1 -0
- package/dist/collection/components/le-stack/le-stack.default.css +37 -0
- package/dist/collection/components/le-stack/le-stack.js +389 -0
- package/dist/collection/components/le-stack/le-stack.js.map +1 -0
- package/dist/collection/components/le-string-input/le-string-input.css +83 -0
- package/dist/collection/components/le-string-input/le-string-input.js +359 -0
- package/dist/collection/components/le-string-input/le-string-input.js.map +1 -0
- package/dist/collection/components/le-text/le-text.default.css +169 -0
- package/dist/collection/components/le-text/le-text.js +475 -0
- package/dist/collection/components/le-text/le-text.js.map +1 -0
- package/dist/collection/components/le-turntable/le-turntable.css +10 -0
- package/dist/collection/components/le-turntable/le-turntable.js +210 -0
- package/dist/collection/components/le-turntable/le-turntable.js.map +1 -0
- package/dist/collection/global/app.js +167 -0
- package/dist/collection/global/app.js.map +1 -0
- package/dist/collection/index.js +15 -0
- package/dist/collection/index.js.map +1 -0
- package/dist/collection/types/blocks.js +115 -0
- package/dist/collection/types/blocks.js.map +1 -0
- package/dist/collection/types/options.js +2 -0
- package/dist/collection/types/options.js.map +1 -0
- package/dist/collection/utils/utils.js +141 -0
- package/dist/collection/utils/utils.js.map +1 -0
- package/dist/components/index.js +127 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/le-box.js +256 -0
- package/dist/components/le-box.js.map +1 -0
- package/dist/components/le-button.js +9 -0
- package/dist/components/le-button.js.map +1 -0
- package/dist/components/le-button2.js +1446 -0
- package/dist/components/le-button2.js.map +1 -0
- package/dist/components/le-card.js +83 -0
- package/dist/components/le-card.js.map +1 -0
- package/dist/components/le-checkbox.js +9 -0
- package/dist/components/le-checkbox.js.map +1 -0
- package/dist/components/le-component.js +9 -0
- package/dist/components/le-component.js.map +1 -0
- package/dist/components/le-number-input.js +271 -0
- package/dist/components/le-number-input.js.map +1 -0
- package/dist/components/le-popover.js +9 -0
- package/dist/components/le-popover.js.map +1 -0
- package/dist/{le-kit/le-popover.entry.js → components/le-popover2.js} +45 -9
- package/dist/components/le-popover2.js.map +1 -0
- package/dist/components/le-popup.js +279 -0
- package/dist/components/le-popup.js.map +1 -0
- package/dist/components/le-round-progress.js +135 -0
- package/dist/components/le-round-progress.js.map +1 -0
- package/dist/components/le-slot.js +9 -0
- package/dist/components/le-slot.js.map +1 -0
- package/dist/components/le-stack.js +198 -0
- package/dist/components/le-stack.js.map +1 -0
- package/dist/components/le-string-input.js +9 -0
- package/dist/components/le-string-input.js.map +1 -0
- package/dist/components/le-text.js +398 -0
- package/dist/components/le-text.js.map +1 -0
- package/dist/components/le-turntable.js +164 -0
- package/dist/components/le-turntable.js.map +1 -0
- package/dist/docs.d.ts +443 -0
- package/dist/docs.json +5185 -0
- package/dist/esm/index-PS-3Rz-c.js +1818 -0
- package/dist/esm/index-PS-3Rz-c.js.map +1 -0
- package/dist/esm/index.js +106 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/{le-kit → esm}/le-box.entry.js +3 -3
- package/dist/esm/le-box.entry.js.map +1 -0
- package/dist/esm/le-button.le-checkbox.le-component.le-popover.le-slot.le-string-input.entry.js.map +1 -0
- package/dist/esm/le-button_6.entry.js +1193 -0
- package/dist/{le-kit → esm}/le-card.entry.js +3 -3
- package/dist/esm/le-card.entry.js.map +1 -0
- package/dist/esm/le-kit.js +21 -0
- package/dist/esm/le-kit.js.map +1 -0
- package/dist/{le-kit → esm}/le-number-input.entry.js +5 -5
- package/dist/esm/le-number-input.entry.js.map +1 -0
- package/dist/{le-kit → esm}/le-popup.entry.js +6 -6
- package/dist/esm/le-popup.entry.js.map +1 -0
- package/dist/{le-kit → esm}/le-round-progress.entry.js +2 -2
- package/dist/esm/le-round-progress.entry.js.map +1 -0
- package/dist/{le-kit → esm}/le-stack.entry.js +3 -3
- package/dist/esm/le-stack.entry.js.map +1 -0
- package/dist/{le-kit → esm}/le-text.entry.js +3 -3
- package/dist/esm/le-text.entry.js.map +1 -0
- package/dist/{le-kit → esm}/le-turntable.entry.js +2 -2
- package/dist/esm/le-turntable.entry.js.map +1 -0
- package/dist/esm/loader.js +11 -0
- package/dist/esm/loader.js.map +1 -0
- package/dist/{le-kit/utils-FDOApZ53.js → esm/utils-lgjSfQP0.js} +3 -3
- package/dist/{le-kit/utils-FDOApZ53.js.map → esm/utils-lgjSfQP0.js.map} +1 -1
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/le-kit/index.esm.js +2 -116
- package/dist/le-kit/index.esm.js.map +1 -1
- package/dist/le-kit/le-button.le-checkbox.le-component.le-popover.le-slot.le-string-input.entry.esm.js.map +1 -0
- package/dist/le-kit/le-kit.css +1 -1010
- package/dist/le-kit/le-kit.esm.js +2 -48
- package/dist/le-kit/le-kit.esm.js.map +1 -1
- package/dist/le-kit/p-27710b5b.entry.js +2 -0
- package/dist/le-kit/p-27710b5b.entry.js.map +1 -0
- package/dist/le-kit/p-34102cef.entry.js +2 -0
- package/dist/le-kit/p-34102cef.entry.js.map +1 -0
- package/dist/le-kit/p-56a80e6d.entry.js +2 -0
- package/dist/le-kit/p-56a80e6d.entry.js.map +1 -0
- package/dist/le-kit/p-615ea10f.entry.js +2 -0
- package/dist/le-kit/p-615ea10f.entry.js.map +1 -0
- package/dist/le-kit/p-935bb2d4.entry.js +2 -0
- package/dist/le-kit/p-935bb2d4.entry.js.map +1 -0
- package/dist/le-kit/p-9d3dc4e5.entry.js +2 -0
- package/dist/le-kit/p-9d3dc4e5.entry.js.map +1 -0
- package/dist/le-kit/p-DN2JVY-7.js +2 -0
- package/dist/le-kit/p-DN2JVY-7.js.map +1 -0
- package/dist/le-kit/p-PS-3Rz-c.js +3 -0
- package/dist/le-kit/p-PS-3Rz-c.js.map +1 -0
- package/dist/le-kit/p-ccabc638.entry.js +2 -0
- package/dist/le-kit/p-ccabc638.entry.js.map +1 -0
- package/dist/le-kit/p-d8157b06.entry.js +2 -0
- package/dist/le-kit/p-d8157b06.entry.js.map +1 -0
- package/dist/le-kit/p-e8c2ca0e.entry.js +2 -0
- package/dist/le-kit/p-e8c2ca0e.entry.js.map +1 -0
- package/dist/themes/base.css +89 -0
- package/dist/themes/dark.css +100 -0
- package/dist/themes/default.css +108 -0
- package/dist/themes/gradient.css +100 -0
- package/dist/themes/index.css +413 -0
- package/dist/themes/minimal.css +100 -0
- package/dist/themes/warm.css +100 -0
- package/dist/types/components.d.ts +4 -4
- package/dist/types/global/app.d.ts +33 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +3 -2
- package/readme.md +22 -0
- package/dist/le-kit/index-Da-89pOc.js +0 -4522
- package/dist/le-kit/index-Da-89pOc.js.map +0 -1
- package/dist/le-kit/le-button.entry.esm.js.map +0 -1
- package/dist/le-kit/le-button.entry.js +0 -90
- package/dist/le-kit/le-checkbox.entry.esm.js.map +0 -1
- package/dist/le-kit/le-checkbox.entry.js +0 -59
- package/dist/le-kit/le-component.entry.esm.js.map +0 -1
- package/dist/le-kit/le-popover.entry.esm.js.map +0 -1
- package/dist/le-kit/le-slot.entry.esm.js.map +0 -1
- package/dist/le-kit/le-string-input.entry.esm.js.map +0 -1
- package/dist/le-kit/le-string-input.entry.js +0 -93
|
@@ -0,0 +1,1200 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('./index-D7B9TPh8.js');
|
|
4
|
+
var utils = require('./utils-DrsoID-a.js');
|
|
5
|
+
|
|
6
|
+
const leButtonDefaultCss = ":host{display:inline-block;--le-button-border-radius:var(--le-radius-md);--le-button-padding-x:0.4rem;--le-button-padding-y:0.4rem;--le-button-small-padding:0.25rem;--le-button-font-size:var(--le-font-size-md);--le-button-font-weight:var(--le-font-weight-medium);--le-button-transition:var(--le-transition-fast);--le-button-icon-aspect-ratio:1;--_btn-bg:var(--le-color-primary);--_btn-bg-hover:var(--le-color-primary-dark);--_btn-bg-system:var(--le-color-black);--_btn-color:var(--le-color-primary-contrast);--_btn-border-color:var(--le-color-primary)}:host([full-width]){display:block;width:100%}.button{display:inline-flex;align-items:center;justify-content:center;gap:var(--le-spacing-3);width:100%;padding:var(--le-button-padding-y) var(--le-button-padding-x);border:1px solid var(--_btn-border-color);border-radius:var(--le-button-border-radius);background:var(--_btn-bg);color:var(--_btn-color);font-family:var(--le-font-family-base);font-size:var(--le-button-font-size);font-weight:var(--le-button-font-weight);line-height:var(--le-line-height-tight);text-decoration:none;cursor:pointer;transition:background-color var(--le-button-transition) var(--le-transition-easing),\n border-color var(--le-button-transition) var(--le-transition-easing),\n box-shadow var(--le-button-transition) var(--le-transition-easing),\n transform var(--le-button-transition) var(--le-transition-easing)}.button:hover:not(:disabled){background:var(--_btn-bg-hover);border-color:var(--_btn-bg-hover)}.button:active:not(:disabled){transform:translateY(1px)}.button:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.button:disabled{opacity:0.5;cursor:not-allowed}:host>le-component.color-primary{--_btn-bg:var(--le-color-primary);--_btn-bg-hover:var(--le-color-primary-dark);--_btn-color:var(--le-color-primary-contrast);--_btn-border-color:var(--le-color-primary)}:host>le-component.color-secondary{--_btn-bg:var(--le-color-secondary);--_btn-bg-hover:var(--le-color-secondary-dark);--_btn-color:var(--le-color-secondary-contrast);--_btn-border-color:var(--le-color-secondary)}:host>le-component.color-success{--_btn-bg:var(--le-color-success);--_btn-bg-hover:var(--le-color-success-dark);--_btn-color:var(--le-color-success-contrast);--_btn-border-color:var(--le-color-success)}:host>le-component.color-warning{--_btn-bg:var(--le-color-warning);--_btn-bg-hover:var(--le-color-warning-dark);--_btn-color:var(--le-color-warning-contrast);--_btn-border-color:var(--le-color-warning)}:host>le-component.color-danger{--_btn-bg:var(--le-color-danger);--_btn-bg-hover:var(--le-color-danger-dark);--_btn-color:var(--le-color-danger-contrast);--_btn-border-color:var(--le-color-danger)}:host>le-component.color-info{--_btn-bg:var(--le-color-info);--_btn-bg-hover:var(--le-color-info-dark);--_btn-color:var(--le-color-info-contrast);--_btn-border-color:var(--le-color-info)}:host>le-component.variant-solid .button{box-shadow:var(--le-shadow-sm)}:host>le-component.variant-solid .button:hover:not(:disabled){box-shadow:var(--le-shadow-md)}:host>le-component.variant-outlined .button{background:transparent;color:var(--_btn-bg);border-color:var(--_btn-border-color)}:host>le-component.variant-outlined .button:hover:not(:disabled){background:var(--_btn-bg);color:var(--_btn-color)}:host>le-component.variant-clear .button{background:transparent;color:var(--_btn-bg);border-color:transparent}:host>le-component.variant-clear .button:hover:not(:disabled){background:var(--le-color-gray-100);border-color:transparent}:host>le-component.variant-system .button{background:transparent;color:var(--_btn-bg-system);border-color:transparent}:host>le-component.size-small .button{--le-button-padding-x:0.4rem;--le-button-padding-y:0.3rem;--le-button-padding-top:0.35rem;--le-button-font-size:var(--le-button-small-font-size, var(--le-font-size-xs))}:host>le-component.size-large .button{--le-button-padding-x:0.9rem;--le-button-padding-y:0.6rem;--le-button-font-size:var(--le-font-size-xl)}:host>le-component.full-width{display:block;width:100%}:host>le-component.selected .button{box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.2)}:host>le-component.variant-outlined.selected .button,:host>le-component.variant-clear.selected .button{background:var(--_btn-bg);color:var(--_btn-color)}:host>le-component.icon-only .button{padding:0.5rem;padding-bottom:0.6rem;aspect-ratio:var(--le-button-icon-aspect-ratio, 1)}:host>le-component.icon-only.size-small .button{padding:var(--le-button-small-padding, 0.25rem)}:host>le-component.icon-only.size-large .button{padding:0.75rem}:host>le-component.icon-only .content{display:none}.content{display:inline}.content:empty{display:none}.icon-start,.icon-only,.icon-end{display:flex;align-items:center;justify-content:center}.icon-start:empty,.icon-only:empty,.icon-end:empty{display:none}::slotted([slot=\"icon-start\"]),::slotted([slot=\"icon-only\"]),::slotted([slot=\"icon-end\"]){display:flex;align-items:center;justify-content:center;width:1.125em;height:1.125em}";
|
|
7
|
+
|
|
8
|
+
const LeButton = class {
|
|
9
|
+
constructor(hostRef) {
|
|
10
|
+
index.registerInstance(this, hostRef);
|
|
11
|
+
this.leClick = index.createEvent(this, "click");
|
|
12
|
+
}
|
|
13
|
+
get el() { return index.getElement(this); }
|
|
14
|
+
/**
|
|
15
|
+
* Mode of the popover should be 'default' for internal use
|
|
16
|
+
*/
|
|
17
|
+
mode;
|
|
18
|
+
/**
|
|
19
|
+
* Button variant style
|
|
20
|
+
* @allowedValues solid | outlined | clear
|
|
21
|
+
*/
|
|
22
|
+
variant = 'solid';
|
|
23
|
+
/**
|
|
24
|
+
* Button color theme (uses theme semantic colors)
|
|
25
|
+
* @allowedValues primary | secondary | success | warning | danger | info
|
|
26
|
+
*/
|
|
27
|
+
color = 'primary';
|
|
28
|
+
/**
|
|
29
|
+
* Button size
|
|
30
|
+
* @allowedValues small | medium | large
|
|
31
|
+
*/
|
|
32
|
+
size = 'medium';
|
|
33
|
+
/**
|
|
34
|
+
* Whether the button is in a selected/active state
|
|
35
|
+
*/
|
|
36
|
+
selected = false;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the button takes full width of its container
|
|
39
|
+
*/
|
|
40
|
+
fullWidth = false;
|
|
41
|
+
/**
|
|
42
|
+
* Whether the button displays only an icon (square aspect ratio)
|
|
43
|
+
*/
|
|
44
|
+
iconOnly = false;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the button is disabled
|
|
47
|
+
*/
|
|
48
|
+
disabled = false;
|
|
49
|
+
/**
|
|
50
|
+
* The button type attribute
|
|
51
|
+
* @allowedValues button | submit | reset
|
|
52
|
+
*/
|
|
53
|
+
type = 'button';
|
|
54
|
+
/**
|
|
55
|
+
* Optional href to make the button act as a link
|
|
56
|
+
*/
|
|
57
|
+
href;
|
|
58
|
+
/**
|
|
59
|
+
* Link target when href is set
|
|
60
|
+
*/
|
|
61
|
+
target;
|
|
62
|
+
/**
|
|
63
|
+
* Emitted when the button is clicked.
|
|
64
|
+
* This is a custom event that wraps the native click but ensures the target is the le-button.
|
|
65
|
+
*/
|
|
66
|
+
leClick;
|
|
67
|
+
handleClick = (event) => {
|
|
68
|
+
// We stop the internal button click from bubbling up
|
|
69
|
+
event.stopPropagation();
|
|
70
|
+
if (this.disabled) {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// And emit our own click event from the host element
|
|
75
|
+
this.leClick.emit(event);
|
|
76
|
+
};
|
|
77
|
+
render() {
|
|
78
|
+
const classes = utils.classnames(`variant-${this.variant}`, `color-${this.color}`, `size-${this.size}`, {
|
|
79
|
+
'selected': this.selected,
|
|
80
|
+
'full-width': this.fullWidth,
|
|
81
|
+
'icon-only': this.iconOnly,
|
|
82
|
+
'disabled': this.disabled,
|
|
83
|
+
});
|
|
84
|
+
const TagType = this.href ? 'a' : 'button';
|
|
85
|
+
const attrs = this.href ? { href: this.href, target: this.target, role: 'button' } : { type: this.type, disabled: this.disabled };
|
|
86
|
+
return (index.h("le-component", { key: '363dbfddf4a765bfd6f36ed16d0912e281153806', component: "le-button", hostClass: classes }, index.h(TagType, { key: 'e112acdff4278e976ad767bd1ea7c9ced5e85f43', class: "button", part: "button", ...attrs, onClick: this.handleClick }, this.iconOnly ? (index.h("span", { class: "icon-start" }, index.h("slot", { name: "icon-only" }))) : (index.h(index.Fragment, null, index.h("span", { class: "icon-start" }, index.h("slot", { name: "icon-start" })), index.h("le-slot", { name: "", description: "Button text", type: "text", class: "content", part: "content" }, index.h("slot", null)), index.h("span", { class: "icon-end" }, index.h("slot", { name: "icon-end" })))))));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
LeButton.style = leButtonDefaultCss;
|
|
90
|
+
|
|
91
|
+
const leCheckboxCss = ":host{display:block;--le-checkbox-size:18px;--le-checkbox-color:var(--le-color-primary, #007bff);--le-checkbox-label-color:var(--le-color-text-primary, #333);--le-checkbox-desc-color:var(--le-color-text-secondary, #666);--le-checkbox-border-radius:var(--le-radius-sm, 2px);--le-checkbox-marker-color:var(--le-color-surface, #fff)}.le-checkbox-wrapper{display:flex;flex-direction:column;gap:4px}.le-checkbox-label{display:inline-flex;align-items:flex-start;gap:8px;cursor:pointer;user-select:none}:host([disabled]) .le-checkbox-label{cursor:not-allowed;opacity:0.6}.le-checkbox-input{display:flex;align-items:center;justify-content:center;min-height:1.4em}input[type=\"checkbox\"]{appearance:none;-webkit-appearance:none;width:var(--le-checkbox-size);height:var(--le-checkbox-size);border:var(--le-border-width, 2px) solid var(--le-checkbox-color);border-radius:var(--le-checkbox-border-radius);margin:0;margin-top:2px;position:relative;cursor:inherit;background-color:transparent;transition:background-color 0.2s, border-color 0.2s}input[type=\"checkbox\"]:checked{background-color:var(--le-checkbox-color)}input[type=\"checkbox\"]:checked::after{content:'';position:absolute;left:0;top:0;bottom:calc(var(--le-checkbox-size) / 5);right:0;margin:auto;width:calc(var(--le-checkbox-size) / 4);height:calc(var(--le-checkbox-size) / 2);border:solid var(--le-checkbox-marker-color, #fff);border-width:0 calc(var(--le-checkbox-size) / 10) calc(var(--le-checkbox-size) / 10) 0;transform:rotate(45deg)}input[type=\"checkbox\"]:focus-visible{outline:2px solid var(--le-color-focus);outline-offset:2px}.le-checkbox-text{flex:1;flex-wrap:wrap;color:var(--le-checkbox-label-color);line-height:1.5;text-align:start}.le-checkbox-description{margin-left:calc(var(--le-checkbox-size) + 8px);font-size:0.875em;color:var(--le-checkbox-desc-color);line-height:1.4}:host [slot=\"description\"]{margin:0}";
|
|
92
|
+
|
|
93
|
+
const LeCheckbox = class {
|
|
94
|
+
constructor(hostRef) {
|
|
95
|
+
index.registerInstance(this, hostRef);
|
|
96
|
+
this.leChange = index.createEvent(this, "change");
|
|
97
|
+
}
|
|
98
|
+
get el() { return index.getElement(this); }
|
|
99
|
+
/**
|
|
100
|
+
* Whether the checkbox is checked
|
|
101
|
+
*/
|
|
102
|
+
checked = false;
|
|
103
|
+
/**
|
|
104
|
+
* Whether the checkbox is disabled
|
|
105
|
+
*/
|
|
106
|
+
disabled = false;
|
|
107
|
+
/**
|
|
108
|
+
* The name of the checkbox input
|
|
109
|
+
*/
|
|
110
|
+
name;
|
|
111
|
+
/**
|
|
112
|
+
* The value of the checkbox input
|
|
113
|
+
*/
|
|
114
|
+
value;
|
|
115
|
+
/**
|
|
116
|
+
* External ID for linking with external systems (e.g. database ID, PDF form field ID)
|
|
117
|
+
*/
|
|
118
|
+
externalId;
|
|
119
|
+
/**
|
|
120
|
+
* Emitted when the checked state changes
|
|
121
|
+
*/
|
|
122
|
+
leChange;
|
|
123
|
+
handleChange = (event) => {
|
|
124
|
+
// We stop the internal button click from bubbling up
|
|
125
|
+
event.stopPropagation();
|
|
126
|
+
if (this.disabled) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const input = event.target;
|
|
131
|
+
this.checked = input.checked;
|
|
132
|
+
this.leChange.emit({
|
|
133
|
+
checked: this.checked,
|
|
134
|
+
value: this.value,
|
|
135
|
+
name: this.name,
|
|
136
|
+
externalId: this.externalId
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
render() {
|
|
140
|
+
return (index.h("le-component", { key: '43399929e07835e0019d509803e50a151921fa72', component: "le-checkbox", hostClass: utils.classnames({ 'disabled': this.disabled }) }, index.h("div", { key: '7ddbf2ac1690bb09082adfea70b9767c972d007a', class: "le-checkbox-wrapper" }, index.h("label", { key: '8eec4055c713e8b3b155695751b10bff64c9f903', class: "le-checkbox-label" }, index.h("span", { key: '2118b1cbe7911ff1674e522d723949d81cade185', class: "le-checkbox-input" }, index.h("input", { key: 'd0a30af5c14497fa6fa294c07ba74ae2e032481f', type: "checkbox", name: this.name, value: this.value, checked: this.checked, disabled: this.disabled, onChange: this.handleChange })), index.h("span", { key: '02cf9588431240039a53ee50e02b08ba5d63b974', class: "le-checkbox-text" }, index.h("le-slot", { key: 'e7d7b253deab72e627164eb72fc06109abfca6a5', name: "", type: "text", tag: "span" }, index.h("slot", { key: '1d8c443073e48848513a8a6d04cd7805a394e54e' })))), index.h("div", { key: '16c2c927dc0c0f7844a203a0628bf0e561009bd0', class: "le-checkbox-description" }, index.h("le-slot", { key: 'c6898ecc8992dce4786e68ab4b136bf5c3a4d3aa', name: "description", type: "text", tag: "div", label: "Description" }, index.h("slot", { key: '3342add8ed1400ab74681e445163eeb3dd415941', name: "description" }))))));
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
LeCheckbox.style = leCheckboxCss;
|
|
144
|
+
|
|
145
|
+
const leComponentCss = ":host{display:contents}:host(.admin-mode){display:block}.le-component-wrapper{position:relative;border:2px dashed var(--le-admin-border-color, #90caf9);border-radius:var(--le-radius-md, 8px);background:var(--le-admin-bg, rgba(144, 202, 249, 0.05));transition:border-color 0.2s ease, box-shadow 0.2s ease}.le-component-wrapper:hover{border-color:var(--le-admin-border-hover, #42a5f5);box-shadow:0 0 0 2px var(--le-admin-glow, rgba(66, 165, 245, 0.2))}.le-component-header{display:flex;align-items:center;justify-content:space-between;gap:var(--le-spacing-1, 4px);padding:0 0 0 var(--le-spacing-1, 4px);background:var(--le-admin-header-bg, rgba(144, 202, 249, 0.15));border-bottom:1px solid var(--le-admin-border-color, #90caf9);border-radius:var(--le-radius-md, 8px) var(--le-radius-md, 8px) 0 0;font-size:var(--le-font-size-xs, 11px)}.le-component-name{font-weight:var(--le-font-weight-medium, 500);color:var(--le-admin-text, #1976d2);text-transform:capitalize;text-align:start;overflow:hidden;width:0;flex:1 1 0%}.le-component-content{padding:var(--le-space-xs, 4px)}.le-component-trigger{font-size:24px;line-height:0px;width:12px;height:12px}.le-component-button{width:20px}.property-editor{display:flex;flex-direction:column;gap:var(--le-space-sm, 8px);max-width:380px}.property-field{display:flex;flex-direction:column;gap:var(--le-space-xs, 4px)}.property-field label{display:flex;flex-direction:column;gap:2px;font-size:var(--le-font-size-sm, 13px);font-weight:var(--le-font-weight-medium, 500);color:var(--le-color-text, #333)}.property-hint{font-size:var(--le-font-size-xs, 11px);font-weight:normal;color:var(--le-color-text-secondary, #666);line-height:1.3}.property-field input[type=\"text\"],.property-field input[type=\"number\"],.property-field select{padding:var(--le-space-xs, 4px) var(--le-space-sm, 8px);border:1px solid var(--le-color-border, #ddd);border-radius:var(--le-radius-md, 7px);font-size:var(--le-font-size-sm, 13px);font-family:inherit;background:var(--le-color-surface, #fff);color:var(--le-color-text, #333);transition:border-color 0.15s ease, box-shadow 0.15s ease}.property-field input:focus,.property-field select:focus{outline:none;border-color:var(--le-color-primary, #1976d2);box-shadow:0 0 0 2px var(--le-color-primary-light, rgba(25, 118, 210, 0.2))}.property-field--checkbox{flex-direction:column}.property-field--checkbox label{flex-direction:row;align-items:center;gap:var(--le-space-sm, 8px);cursor:pointer}.property-field--checkbox input[type=\"checkbox\"]{width:16px;height:16px;margin:0;cursor:pointer;accent-color:var(--le-color-primary, #1976d2)}.property-field--checkbox .property-hint{margin-left:24px}.no-properties{margin:0;padding:var(--le-space-sm, 8px);font-size:var(--le-font-size-sm, 13px);color:var(--le-color-text-secondary, #666);text-align:center}.property-editor-container{display:flex;flex-direction:column;gap:var(--le-space-md, 12px)}.property-editor-actions{padding-top:var(--le-space-sm, 8px);border-top:1px solid var(--le-color-border, #e5e5e5)}.delete-component-btn{display:flex;align-items:center;justify-content:center;gap:var(--le-space-xs, 4px);width:100%;padding:var(--le-space-sm, 8px) var(--le-space-md, 12px);border:1px solid var(--le-color-danger, #e53935);border-radius:var(--le-radius-md, 6px);background:transparent;color:var(--le-color-danger, #e53935);font-size:var(--le-font-size-sm, 13px);font-weight:500;cursor:pointer;transition:background-color 0.15s, color 0.15s}.delete-component-btn:hover{background:var(--le-color-danger, #e53935);color:white}.delete-component-btn:active{opacity:0.9}";
|
|
146
|
+
|
|
147
|
+
const LeComponent = class {
|
|
148
|
+
constructor(hostRef) {
|
|
149
|
+
index.registerInstance(this, hostRef);
|
|
150
|
+
}
|
|
151
|
+
get el() { return index.getElement(this); }
|
|
152
|
+
/**
|
|
153
|
+
* The tag name of the component (e.g., 'le-card').
|
|
154
|
+
* Used to look up property metadata and display the component name.
|
|
155
|
+
*/
|
|
156
|
+
component;
|
|
157
|
+
/**
|
|
158
|
+
* Optional display name for the component.
|
|
159
|
+
* If not provided, the tag name will be formatted as the display name.
|
|
160
|
+
*/
|
|
161
|
+
displayName;
|
|
162
|
+
/**
|
|
163
|
+
* Classes to apply to the host element.
|
|
164
|
+
* Allows parent components to pass their styling classes.
|
|
165
|
+
*/
|
|
166
|
+
hostClass;
|
|
167
|
+
/**
|
|
168
|
+
* Inline styles to apply to the host element.
|
|
169
|
+
* Allows parent components to pass dynamic styles (e.g., flex properties).
|
|
170
|
+
*/
|
|
171
|
+
hostStyle;
|
|
172
|
+
/**
|
|
173
|
+
* Reference to the host element (found automatically from parent)
|
|
174
|
+
*/
|
|
175
|
+
hostElement;
|
|
176
|
+
/**
|
|
177
|
+
* Internal state to track admin mode
|
|
178
|
+
*/
|
|
179
|
+
adminMode = false;
|
|
180
|
+
/**
|
|
181
|
+
* Component metadata loaded from Custom Elements Manifest
|
|
182
|
+
*/
|
|
183
|
+
componentMeta = null;
|
|
184
|
+
/**
|
|
185
|
+
* Current property values of the host component
|
|
186
|
+
*/
|
|
187
|
+
propertyValues = {};
|
|
188
|
+
disconnectModeObserver;
|
|
189
|
+
connectedCallback() {
|
|
190
|
+
// Find the host element - le-component is rendered inside the component's shadow DOM,
|
|
191
|
+
// so we need to find the shadow root's host element
|
|
192
|
+
this.findHostElement();
|
|
193
|
+
this.disconnectModeObserver = utils.observeModeChanges(this.el, mode => {
|
|
194
|
+
this.adminMode = mode === 'admin';
|
|
195
|
+
// Load metadata and refresh property values only when entering admin mode
|
|
196
|
+
if (this.adminMode) {
|
|
197
|
+
if (!this.componentMeta) {
|
|
198
|
+
this.loadComponentMetadata();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
this.readPropertyValues();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Find the host element by traversing up through shadow DOM
|
|
208
|
+
*/
|
|
209
|
+
findHostElement() {
|
|
210
|
+
// Get the shadow root that contains this le-component
|
|
211
|
+
const rootNode = this.el.getRootNode();
|
|
212
|
+
if (rootNode instanceof ShadowRoot) {
|
|
213
|
+
// The host of this shadow root is our target component (e.g., le-card)
|
|
214
|
+
this.hostElement = rootNode.host;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
componentDidLoad() {
|
|
218
|
+
// Read initial property values from the host element
|
|
219
|
+
this.readPropertyValues();
|
|
220
|
+
}
|
|
221
|
+
disconnectedCallback() {
|
|
222
|
+
this.disconnectModeObserver?.();
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Formats a tag name into a display name
|
|
226
|
+
* e.g., 'le-card' -> 'Card'
|
|
227
|
+
*/
|
|
228
|
+
formatDisplayName(tagName) {
|
|
229
|
+
return tagName
|
|
230
|
+
.replace(/^le-/, '') // Remove 'le-' prefix
|
|
231
|
+
.split('-')
|
|
232
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
233
|
+
.join(' ');
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Load component metadata from the Custom Elements Manifest
|
|
237
|
+
*/
|
|
238
|
+
async loadComponentMetadata() {
|
|
239
|
+
try {
|
|
240
|
+
// Fetch the manifest from configured URL
|
|
241
|
+
const { manifestUrl } = index.getLeKitConfig();
|
|
242
|
+
const response = await fetch(manifestUrl);
|
|
243
|
+
const manifest = await response.json();
|
|
244
|
+
// Find the component definition
|
|
245
|
+
for (const module of manifest.modules) {
|
|
246
|
+
for (const declaration of module.declarations || []) {
|
|
247
|
+
if (declaration.tagName === this.component) {
|
|
248
|
+
const attributes = (declaration.attributes || []).filter((attr) => !this.isInternalAttribute(attr.name));
|
|
249
|
+
this.componentMeta = {
|
|
250
|
+
tagName: declaration.tagName,
|
|
251
|
+
description: declaration.description,
|
|
252
|
+
attributes,
|
|
253
|
+
};
|
|
254
|
+
// console.log(`[le-component] Loaded metadata for ${this.component}:`, this.componentMeta);
|
|
255
|
+
// Read property values after metadata is loaded
|
|
256
|
+
this.readPropertyValues();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// console.warn(`[le-component] No metadata found for component: ${this.component}`);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
// console.warn(`[le-component] Failed to load metadata for component: ${this.component}`, error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Check if an attribute is internal (should not be shown in editor)
|
|
269
|
+
*/
|
|
270
|
+
isInternalAttribute(name) {
|
|
271
|
+
const internalAttrs = ['mode', 'theme', 'class', 'style', 'id', 'slot'];
|
|
272
|
+
return internalAttrs.includes(name);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Read current property values from the host element
|
|
276
|
+
*/
|
|
277
|
+
readPropertyValues() {
|
|
278
|
+
if (!this.hostElement || !this.componentMeta)
|
|
279
|
+
return;
|
|
280
|
+
const values = {};
|
|
281
|
+
for (const attr of this.componentMeta.attributes) {
|
|
282
|
+
const value = this.hostElement.getAttribute(attr.name);
|
|
283
|
+
values[attr.name] = this.parseAttributeValue(value, attr.type?.text);
|
|
284
|
+
}
|
|
285
|
+
this.propertyValues = values;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Parse an attribute value based on its type
|
|
289
|
+
*/
|
|
290
|
+
parseAttributeValue(value, type) {
|
|
291
|
+
if (value === null)
|
|
292
|
+
return undefined;
|
|
293
|
+
if (type === 'boolean') {
|
|
294
|
+
return value !== null && value !== 'false';
|
|
295
|
+
}
|
|
296
|
+
if (type === 'number') {
|
|
297
|
+
return parseFloat(value);
|
|
298
|
+
}
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Handle property value changes from the editor
|
|
303
|
+
*/
|
|
304
|
+
handlePropertyChange(attrName, value, type) {
|
|
305
|
+
if (!this.hostElement)
|
|
306
|
+
return;
|
|
307
|
+
// Update the host element's attribute
|
|
308
|
+
if (type === 'boolean') {
|
|
309
|
+
if (value) {
|
|
310
|
+
this.hostElement.setAttribute(attrName, '');
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
this.hostElement.removeAttribute(attrName);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (value === undefined || value === '') {
|
|
317
|
+
this.hostElement.removeAttribute(attrName);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.hostElement.setAttribute(attrName, String(value));
|
|
321
|
+
}
|
|
322
|
+
// Update local state
|
|
323
|
+
this.propertyValues = { ...this.propertyValues, [attrName]: value };
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Delete this component from the DOM
|
|
327
|
+
*/
|
|
328
|
+
deleteComponent() {
|
|
329
|
+
if (!this.hostElement)
|
|
330
|
+
return;
|
|
331
|
+
// Confirm deletion
|
|
332
|
+
const name = this.displayName || this.formatDisplayName(this.component);
|
|
333
|
+
if (!confirm(`Delete this ${name}?`))
|
|
334
|
+
return;
|
|
335
|
+
// Remove the host element from its parent
|
|
336
|
+
const parent = this.hostElement.parentElement;
|
|
337
|
+
if (parent) {
|
|
338
|
+
this.hostElement.remove();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Render the property editor form
|
|
343
|
+
*/
|
|
344
|
+
renderPropertyEditor() {
|
|
345
|
+
const hasProperties = this.componentMeta && this.componentMeta.attributes.length > 0;
|
|
346
|
+
return (index.h("div", { class: "property-editor-container" }, hasProperties ? (index.h("form", { class: "property-editor", onSubmit: e => e.preventDefault() }, this.componentMeta.attributes.map(attr => this.renderPropertyField(attr)))) : (index.h("p", { class: "no-properties" }, "No editable properties")), index.h("div", { class: "property-editor-actions" }, index.h("le-button", { type: "button", variant: "outlined", color: "danger", "full-width": true, onClick: () => this.deleteComponent() }, index.h("span", { slot: "icon-start" }, "\uD83D\uDDD1\uFE0F"), index.h("span", null, "Delete Component")))));
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Render a single property field based on its type
|
|
350
|
+
*/
|
|
351
|
+
renderPropertyField(attr) {
|
|
352
|
+
const value = this.propertyValues[attr.name];
|
|
353
|
+
const type = attr.type?.text || 'string';
|
|
354
|
+
// Check if type is a union of string literals (e.g., "'default' | 'outlined' | 'elevated'")
|
|
355
|
+
const enumMatch = type.match(/^'[^']+'/);
|
|
356
|
+
if (enumMatch) {
|
|
357
|
+
const options = type.split('|').map(opt => opt.trim().replace(/'/g, ''));
|
|
358
|
+
return (index.h("div", { class: "property-field" }, index.h("label", { htmlFor: `prop-${attr.name}` }, attr.name, attr.description && index.h("span", { class: "property-hint" }, attr.description)), index.h("select", { id: `prop-${attr.name}`, onChange: e => this.handlePropertyChange(attr.name, e.target.value, type) }, options.map(opt => (index.h("option", { value: opt, selected: value === opt || (!value && attr.default?.replace(/'/g, '') === opt) }, opt))))));
|
|
359
|
+
}
|
|
360
|
+
// Boolean type
|
|
361
|
+
if (type === 'boolean') {
|
|
362
|
+
return (index.h("div", { class: "property-field property-field--checkbox" }, index.h("le-checkbox", { name: `prop-${attr.name}`, checked: value === true || value === '', onChange: e => this.handlePropertyChange(attr.name, e.target.checked, type) }, attr.name, attr.description && index.h("div", { slot: "description" }, attr.description))));
|
|
363
|
+
}
|
|
364
|
+
// Number type
|
|
365
|
+
if (type === 'number') {
|
|
366
|
+
return (index.h("div", { class: "property-field" }, index.h("label", { htmlFor: `prop-${attr.name}` }, attr.name, attr.description && index.h("span", { class: "property-hint" }, attr.description)), index.h("input", { type: "number", id: `prop-${attr.name}`, value: value ?? '', placeholder: attr.default, onChange: e => this.handlePropertyChange(attr.name, e.target.value, type) })));
|
|
367
|
+
}
|
|
368
|
+
// Default: string/text input
|
|
369
|
+
return (index.h("div", { class: "property-field" }, index.h("le-string-input", { name: `prop-${attr.name}`, label: attr.name, value: value ?? '', placeholder: attr.default?.replace(/'/g, ''), onChange: (e) => this.handlePropertyChange(attr.name, e.detail.value, type) }, index.h("span", { slot: "description" }, attr.description))));
|
|
370
|
+
}
|
|
371
|
+
render() {
|
|
372
|
+
const name = this.displayName || this.formatDisplayName(this.component);
|
|
373
|
+
// In default mode, just pass through content with host classes
|
|
374
|
+
if (!this.adminMode) {
|
|
375
|
+
return (index.h(index.Host, { class: utils.classnames(this.component, this.hostClass), style: this.hostStyle }, index.h("slot", null)));
|
|
376
|
+
}
|
|
377
|
+
// In admin mode, show wrapper with header and settings
|
|
378
|
+
return (index.h(index.Host, { class: utils.classnames(this.component, this.hostClass, 'admin-mode'), style: this.hostStyle }, index.h("div", { class: "le-component-wrapper" }, index.h("div", { class: "le-component-header" }, index.h("span", { class: "le-component-name" }, name), index.h("le-popover", { popoverTitle: `${name} Settings`, position: "right", align: "start", "min-width": "300px", mode: "default" }, index.h("le-button", { type: "button", class: "le-component-button", slot: "trigger", variant: "clear", size: "small", "aria-label": "Edit component properties", "icon-only": true }, index.h("span", { class: "le-component-trigger", slot: "icon-only" }, "\u2699")), this.renderPropertyEditor())), index.h("div", { class: "le-component-content" }, index.h("slot", null)))));
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
LeComponent.style = leComponentCss;
|
|
382
|
+
|
|
383
|
+
const lePopoverCss = "/* ============================================\n le-popover.css\n Popover using native HTML Popover API\n ============================================ */\n\n:host {\n display: inline-block;\n position: relative;\n}\n\n/* ============================================\n Trigger\n ============================================ */\n\n.le-popover-trigger {\n display: inline-flex;\n cursor: pointer;\n}\n\n.le-popover-default-trigger {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 1px solid var(--le-color-border, #e0e0e0);\n border-radius: var(--le-radius-md, 6px);\n background: var(--le-color-surface, #fff);\n color: var(--le-color-text-secondary, #666);\n font-size: 16px;\n cursor: pointer;\n transition: all var(--le-transition-fast, 0.15s ease);\n}\n\n.le-popover-default-trigger:hover {\n border-color: var(--le-color-primary, #2196f3);\n color: var(--le-color-primary, #2196f3);\n background: var(--le-color-primary-light, rgba(33, 150, 243, 0.1));\n}\n\n/* ============================================\n Popover Content (native popover)\n ============================================ */\n\n.le-popover-content {\n /* Reset native popover defaults */\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n \n /* Positioning - will be set via JS */\n position: fixed;\n inset: unset;\n \n /* Styling */\n background: var(--le-color-surface, #ffffff);\n border: 1px solid var(--le-color-border, #e0e0e0);\n border-radius: var(--le-radius-lg, 8px);\n box-shadow: var(--le-shadow-lg, 0 4px 12px rgba(0, 0, 0, 0.15));\n overflow: hidden;\n font-family: var(--le-font-family, system-ui, -apple-system, sans-serif);\n font-size: var(--le-font-size-sm, 0.875rem);\n color: var(--le-color-text, #333);\n \n /* Animation */\n opacity: 0;\n transform: scale(0.95);\n transition: opacity 0.15s ease, transform 0.15s ease, display 0.15s ease allow-discrete;\n}\n\n/* When popover is open */\n.le-popover-content:popover-open {\n opacity: 1;\n transform: scale(1);\n}\n\n/* Starting style for animation (CSS Anchor Positioning spec) */\n@starting-style {\n .le-popover-content:popover-open {\n opacity: 0;\n transform: scale(0.95);\n }\n}\n\n/* ============================================\n Header\n ============================================ */\n\n.le-popover-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--le-space-xs, 4px) var(--le-space-xs, 4px) var(--le-space-xs, 4px) var(--le-space-sm, 8px);\n border-bottom: 1px solid var(--le-color-border, #e0e0e0);\n background: var(--le-color-surface-alt, #f9f9f9);\n min-height: 32px;\n}\n\n.le-popover-title {\n font-weight: var(--le-font-weight-semibold, 600);\n font-size: var(--le-font-size-sm, 0.875rem);\n color: var(--le-color-text, #333);\n}\n\n.le-popover-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--le-color-text-secondary, #666);\n font-size: 18px;\n line-height: 1;\n cursor: pointer;\n border-radius: var(--le-radius-sm, 4px);\n transition: background-color 0.15s, color 0.15s;\n}\n\n.le-popover-close:hover {\n background: var(--le-color-surface-hover, rgba(0, 0, 0, 0.05));\n color: var(--le-color-text, #333);\n}\n\n/* ============================================\n Body\n ============================================ */\n\n.le-popover-body {\n padding: var(--le-space-md, 12px);\n}\n\n/* ============================================\n Scrollable content\n ============================================ */\n\n.le-popover-content[style*=\"overflow-y: auto\"] .le-popover-body {\n overflow-y: auto;\n}\n";
|
|
384
|
+
|
|
385
|
+
const LePopover = class {
|
|
386
|
+
constructor(hostRef) {
|
|
387
|
+
index.registerInstance(this, hostRef);
|
|
388
|
+
this.lePopoverOpen = index.createEvent(this, "lePopoverOpen");
|
|
389
|
+
this.lePopoverClose = index.createEvent(this, "lePopoverClose");
|
|
390
|
+
}
|
|
391
|
+
get el() { return index.getElement(this); }
|
|
392
|
+
/**
|
|
393
|
+
* Mode of the popover should be 'default' for internal use
|
|
394
|
+
*/
|
|
395
|
+
mode;
|
|
396
|
+
/**
|
|
397
|
+
* Whether the popover is currently open
|
|
398
|
+
*/
|
|
399
|
+
open = false;
|
|
400
|
+
/**
|
|
401
|
+
* Position of the popover relative to its trigger
|
|
402
|
+
*/
|
|
403
|
+
position = 'bottom';
|
|
404
|
+
/**
|
|
405
|
+
* Alignment of the popover
|
|
406
|
+
*/
|
|
407
|
+
align = 'start';
|
|
408
|
+
/**
|
|
409
|
+
* Optional title for the popover header
|
|
410
|
+
*/
|
|
411
|
+
popoverTitle;
|
|
412
|
+
/**
|
|
413
|
+
* Whether to show a close button in the header
|
|
414
|
+
*/
|
|
415
|
+
showClose = true;
|
|
416
|
+
/**
|
|
417
|
+
* Whether clicking outside closes the popover
|
|
418
|
+
*/
|
|
419
|
+
closeOnClickOutside = true;
|
|
420
|
+
/**
|
|
421
|
+
* Whether pressing Escape closes the popover
|
|
422
|
+
*/
|
|
423
|
+
closeOnEscape = true;
|
|
424
|
+
/**
|
|
425
|
+
* Offset from the trigger element (in pixels)
|
|
426
|
+
*/
|
|
427
|
+
offset = 8;
|
|
428
|
+
/**
|
|
429
|
+
* Fixed width for the popover (e.g., '300px', '20rem')
|
|
430
|
+
*/
|
|
431
|
+
width;
|
|
432
|
+
/**
|
|
433
|
+
* Minimum width for the popover (e.g., '200px', '15rem')
|
|
434
|
+
*/
|
|
435
|
+
minWidth = '200px';
|
|
436
|
+
/**
|
|
437
|
+
* Maximum width for the popover (e.g., '400px', '25rem')
|
|
438
|
+
*/
|
|
439
|
+
maxWidth;
|
|
440
|
+
/**
|
|
441
|
+
* Emitted when the popover opens
|
|
442
|
+
*/
|
|
443
|
+
lePopoverOpen;
|
|
444
|
+
/**
|
|
445
|
+
* Emitted when the popover closes
|
|
446
|
+
*/
|
|
447
|
+
lePopoverClose;
|
|
448
|
+
isPositioned = false;
|
|
449
|
+
triggerEl;
|
|
450
|
+
popoverEl;
|
|
451
|
+
uniqueId = `le-popover-${Math.random().toString(36).substr(2, 9)}`;
|
|
452
|
+
scrollParents = [];
|
|
453
|
+
componentDidLoad() {
|
|
454
|
+
// Listen for toggle events from the native popover API
|
|
455
|
+
this.popoverEl?.addEventListener('toggle', this.handlePopoverToggle);
|
|
456
|
+
// Listen for other popovers opening to close this one
|
|
457
|
+
document.addEventListener('le-popover-will-open', this.handleOtherPopoverOpen);
|
|
458
|
+
}
|
|
459
|
+
disconnectedCallback() {
|
|
460
|
+
this.popoverEl?.removeEventListener('toggle', this.handlePopoverToggle);
|
|
461
|
+
document.removeEventListener('le-popover-will-open', this.handleOtherPopoverOpen);
|
|
462
|
+
this.removeScrollListeners();
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Find all scrollable parent elements
|
|
466
|
+
*/
|
|
467
|
+
getScrollParents(element) {
|
|
468
|
+
const scrollParents = [];
|
|
469
|
+
let parent = element.parentElement;
|
|
470
|
+
while (parent) {
|
|
471
|
+
const style = getComputedStyle(parent);
|
|
472
|
+
const overflow = style.overflow + style.overflowY + style.overflowX;
|
|
473
|
+
if (/(auto|scroll)/.test(overflow)) {
|
|
474
|
+
scrollParents.push(parent);
|
|
475
|
+
}
|
|
476
|
+
parent = parent.parentElement;
|
|
477
|
+
}
|
|
478
|
+
// Always include window for page scroll
|
|
479
|
+
return scrollParents;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Add scroll listeners to all scrollable parents
|
|
483
|
+
*/
|
|
484
|
+
addScrollListeners() {
|
|
485
|
+
if (!this.triggerEl)
|
|
486
|
+
return;
|
|
487
|
+
this.scrollParents = this.getScrollParents(this.triggerEl);
|
|
488
|
+
// Listen to each scroll parent
|
|
489
|
+
this.scrollParents.forEach(parent => {
|
|
490
|
+
parent.addEventListener('scroll', this.handleScroll, { passive: true });
|
|
491
|
+
});
|
|
492
|
+
// Also listen to window scroll and resize
|
|
493
|
+
window.addEventListener('scroll', this.handleScroll, { passive: true });
|
|
494
|
+
window.addEventListener('resize', this.handleScroll, { passive: true });
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Remove scroll listeners
|
|
498
|
+
*/
|
|
499
|
+
removeScrollListeners() {
|
|
500
|
+
this.scrollParents.forEach(parent => {
|
|
501
|
+
parent.removeEventListener('scroll', this.handleScroll);
|
|
502
|
+
});
|
|
503
|
+
window.removeEventListener('scroll', this.handleScroll);
|
|
504
|
+
window.removeEventListener('resize', this.handleScroll);
|
|
505
|
+
this.scrollParents = [];
|
|
506
|
+
}
|
|
507
|
+
handleScroll = () => {
|
|
508
|
+
if (this.open) {
|
|
509
|
+
this.updatePosition();
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
handlePopoverToggle = (event) => {
|
|
513
|
+
if (event.newState === 'open') {
|
|
514
|
+
this.open = true;
|
|
515
|
+
this.addScrollListeners();
|
|
516
|
+
this.updatePosition();
|
|
517
|
+
this.lePopoverOpen.emit();
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
this.open = false;
|
|
521
|
+
this.isPositioned = false;
|
|
522
|
+
this.removeScrollListeners();
|
|
523
|
+
this.lePopoverClose.emit();
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
handleOtherPopoverOpen = (event) => {
|
|
527
|
+
const customEvent = event;
|
|
528
|
+
if (customEvent.detail?.popover === this.el)
|
|
529
|
+
return;
|
|
530
|
+
if (this.open) {
|
|
531
|
+
this.hide();
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
/**
|
|
535
|
+
* Opens the popover
|
|
536
|
+
*/
|
|
537
|
+
async show() {
|
|
538
|
+
document.dispatchEvent(new CustomEvent('le-popover-will-open', {
|
|
539
|
+
detail: { popover: this.el }
|
|
540
|
+
}));
|
|
541
|
+
this.popoverEl?.showPopover();
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Closes the popover
|
|
545
|
+
*/
|
|
546
|
+
async hide() {
|
|
547
|
+
this.popoverEl?.hidePopover();
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Toggles the popover
|
|
551
|
+
*/
|
|
552
|
+
async toggle() {
|
|
553
|
+
if (this.open) {
|
|
554
|
+
await this.hide();
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
await this.show();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
handleTriggerClick = (event) => {
|
|
561
|
+
event.stopPropagation();
|
|
562
|
+
this.toggle();
|
|
563
|
+
};
|
|
564
|
+
updatePosition() {
|
|
565
|
+
if (!this.triggerEl || !this.popoverEl)
|
|
566
|
+
return;
|
|
567
|
+
const triggerRect = this.triggerEl.getBoundingClientRect();
|
|
568
|
+
const popoverRect = this.popoverEl.getBoundingClientRect();
|
|
569
|
+
const viewportWidth = window.innerWidth;
|
|
570
|
+
const viewportHeight = window.innerHeight;
|
|
571
|
+
const viewportPadding = 8;
|
|
572
|
+
let position = this.position;
|
|
573
|
+
let align = this.align;
|
|
574
|
+
// Auto-position logic
|
|
575
|
+
const spaceBelow = viewportHeight - triggerRect.bottom - viewportPadding;
|
|
576
|
+
const spaceAbove = triggerRect.top - viewportPadding;
|
|
577
|
+
const spaceRight = viewportWidth - triggerRect.right - viewportPadding;
|
|
578
|
+
const spaceLeft = triggerRect.left - viewportPadding;
|
|
579
|
+
if (position === 'auto') {
|
|
580
|
+
if (spaceBelow >= popoverRect.height + this.offset) {
|
|
581
|
+
position = 'bottom';
|
|
582
|
+
}
|
|
583
|
+
else if (spaceAbove >= popoverRect.height + this.offset) {
|
|
584
|
+
position = 'top';
|
|
585
|
+
}
|
|
586
|
+
else if (spaceRight >= popoverRect.width + this.offset) {
|
|
587
|
+
position = 'right';
|
|
588
|
+
}
|
|
589
|
+
else if (spaceLeft >= popoverRect.width + this.offset) {
|
|
590
|
+
position = 'left';
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
const maxSpace = Math.max(spaceBelow, spaceAbove, spaceRight, spaceLeft);
|
|
594
|
+
if (maxSpace === spaceBelow)
|
|
595
|
+
position = 'bottom';
|
|
596
|
+
else if (maxSpace === spaceAbove)
|
|
597
|
+
position = 'top';
|
|
598
|
+
else if (maxSpace === spaceRight)
|
|
599
|
+
position = 'right';
|
|
600
|
+
else
|
|
601
|
+
position = 'left';
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Adjust alignment for horizontal overflow
|
|
605
|
+
if (position === 'top' || position === 'bottom') {
|
|
606
|
+
if (align === 'start' && triggerRect.left + popoverRect.width > viewportWidth - viewportPadding) {
|
|
607
|
+
align = 'end';
|
|
608
|
+
}
|
|
609
|
+
else if (align === 'end' && triggerRect.right - popoverRect.width < viewportPadding) {
|
|
610
|
+
align = 'start';
|
|
611
|
+
}
|
|
612
|
+
else if (align === 'center') {
|
|
613
|
+
const triggerCenter = triggerRect.left + triggerRect.width / 2;
|
|
614
|
+
if (triggerCenter - popoverRect.width / 2 < viewportPadding) {
|
|
615
|
+
align = 'start';
|
|
616
|
+
}
|
|
617
|
+
else if (triggerCenter + popoverRect.width / 2 > viewportWidth - viewportPadding) {
|
|
618
|
+
align = 'end';
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Calculate position
|
|
623
|
+
let top = 0;
|
|
624
|
+
let left = 0;
|
|
625
|
+
let maxHeight = null;
|
|
626
|
+
switch (position) {
|
|
627
|
+
case 'top':
|
|
628
|
+
top = triggerRect.top - popoverRect.height - this.offset;
|
|
629
|
+
if (top < viewportPadding) {
|
|
630
|
+
maxHeight = triggerRect.top - this.offset - viewportPadding * 2;
|
|
631
|
+
top = viewportPadding;
|
|
632
|
+
}
|
|
633
|
+
break;
|
|
634
|
+
case 'bottom':
|
|
635
|
+
top = triggerRect.bottom + this.offset;
|
|
636
|
+
if (top + popoverRect.height > viewportHeight - viewportPadding) {
|
|
637
|
+
maxHeight = viewportHeight - top - viewportPadding;
|
|
638
|
+
}
|
|
639
|
+
break;
|
|
640
|
+
case 'left':
|
|
641
|
+
left = triggerRect.left - popoverRect.width - this.offset;
|
|
642
|
+
top = triggerRect.top;
|
|
643
|
+
if (left < viewportPadding)
|
|
644
|
+
left = viewportPadding;
|
|
645
|
+
break;
|
|
646
|
+
case 'right':
|
|
647
|
+
left = triggerRect.right + this.offset;
|
|
648
|
+
top = triggerRect.top;
|
|
649
|
+
if (left + popoverRect.width > viewportWidth - viewportPadding) {
|
|
650
|
+
left = viewportWidth - popoverRect.width - viewportPadding;
|
|
651
|
+
}
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
// Calculate horizontal alignment for top/bottom
|
|
655
|
+
if (position === 'top' || position === 'bottom') {
|
|
656
|
+
switch (align) {
|
|
657
|
+
case 'start':
|
|
658
|
+
left = triggerRect.left;
|
|
659
|
+
break;
|
|
660
|
+
case 'center':
|
|
661
|
+
left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
|
|
662
|
+
break;
|
|
663
|
+
case 'end':
|
|
664
|
+
left = triggerRect.right - popoverRect.width;
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
// Constrain to viewport
|
|
668
|
+
if (left < viewportPadding) {
|
|
669
|
+
left = viewportPadding;
|
|
670
|
+
}
|
|
671
|
+
else if (left + popoverRect.width > viewportWidth - viewportPadding) {
|
|
672
|
+
left = viewportWidth - popoverRect.width - viewportPadding;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
// Calculate vertical alignment for left/right
|
|
676
|
+
if (position === 'left' || position === 'right') {
|
|
677
|
+
switch (align) {
|
|
678
|
+
case 'start':
|
|
679
|
+
top = triggerRect.top;
|
|
680
|
+
break;
|
|
681
|
+
case 'center':
|
|
682
|
+
top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
|
|
683
|
+
break;
|
|
684
|
+
case 'end':
|
|
685
|
+
top = triggerRect.bottom - popoverRect.height;
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
if (top < viewportPadding)
|
|
689
|
+
top = viewportPadding;
|
|
690
|
+
if (top + popoverRect.height > viewportHeight - viewportPadding) {
|
|
691
|
+
maxHeight = viewportHeight - top - viewportPadding;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// Apply styles
|
|
695
|
+
this.popoverEl.style.top = `${top}px`;
|
|
696
|
+
this.popoverEl.style.left = `${left}px`;
|
|
697
|
+
if (maxHeight !== null && maxHeight > 100) {
|
|
698
|
+
this.popoverEl.style.maxHeight = `${maxHeight}px`;
|
|
699
|
+
this.popoverEl.style.overflowY = 'auto';
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
this.popoverEl.style.maxHeight = '';
|
|
703
|
+
this.popoverEl.style.overflowY = '';
|
|
704
|
+
}
|
|
705
|
+
this.isPositioned = true;
|
|
706
|
+
}
|
|
707
|
+
render() {
|
|
708
|
+
const popoverStyles = {
|
|
709
|
+
visibility: this.isPositioned ? 'visible' : 'hidden',
|
|
710
|
+
};
|
|
711
|
+
if (this.width)
|
|
712
|
+
popoverStyles.width = this.width;
|
|
713
|
+
if (this.minWidth)
|
|
714
|
+
popoverStyles.minWidth = this.minWidth;
|
|
715
|
+
if (this.maxWidth)
|
|
716
|
+
popoverStyles.maxWidth = this.maxWidth;
|
|
717
|
+
return [
|
|
718
|
+
index.h("div", { key: '3d2a3b86ddb3be577bd0e976a5a00f25396a97fb', class: "le-popover-trigger", ref: (el) => (this.triggerEl = el), onClick: this.handleTriggerClick }, index.h("slot", { key: '9db2e9e2b0bdb25c5a2361115d2094292fe692ec', name: "trigger" }, index.h("button", { key: '88d78b192f72731f00d6131b92b8d6a4b445d7f9', type: "button", class: "le-popover-default-trigger" }, index.h("span", { key: '0f2913f160cda5dcbeb20c714219d587196e230b' }, "\u2295")))),
|
|
719
|
+
index.h("div", { key: 'b65d2de900ff6e67a62d92d7452db34afde9f257', id: this.uniqueId, class: "le-popover-content", popover: this.closeOnClickOutside ? 'auto' : 'manual', ref: (el) => (this.popoverEl = el), style: popoverStyles }, (this.popoverTitle || this.showClose) && (index.h("div", { key: '323061fc49c8e0fc9ae541dfc576e551b0a83818', class: "le-popover-header" }, this.popoverTitle && index.h("span", { key: 'b1d9f4203966ae6a0bb14186824596643954d6f8', class: "le-popover-title" }, this.popoverTitle), this.showClose && (index.h("button", { key: 'd0d335d8c9897ed9ba7bd0370572a8cafa72b044', type: "button", class: "le-popover-close", onClick: () => this.hide(), "aria-label": "Close" }, "\u00D7")))), index.h("div", { key: '52d3294394b0e1fd49f8c361414e228feed68ea9', class: "le-popover-body" }, index.h("slot", { key: '16d8691545f2eaa44f136bfea33a1f1d71d18cd7' })))
|
|
720
|
+
];
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
LePopover.style = lePopoverCss;
|
|
724
|
+
|
|
725
|
+
const leSlotDefaultCss = ":host{display:contents;--le-slot-border-color:#0088ff;--le-slot-bg-color:rgba(0, 136, 255, 0.05);--le-slot-header-bg:rgb(218, 238, 255);--le-slot-label-color:#0066cc;--le-slot-description-color:#666;--le-slot-required-color:#e53935;--le-slot-dropzone-min-height:20px;--le-slot-dropzone-border-color:#ccc}.le-slot-container,.le-slot-header,.le-slot-description,.le-slot-dropzone,.le-slot-input{display:none}.hidden-slot{display:none}:host(.admin-mode){display:block;flex:1}:host(.admin-mode) .le-slot-container{position:relative;display:flex;flex-direction:column;border:2px dashed var(--le-slot-border-color);border-radius:4px;background:var(--le-slot-bg-color);margin:4px 0}:host(.admin-mode) .le-slot-header{display:flex;align-items:center;gap:4px;padding:0 0 0 var(--le-spacing-1, 4px);background:var(--le-slot-header-bg);border-bottom:1px solid var(--le-slot-border-color);font-size:var(--le-font-size-xs, 11px);font-weight:400;text-transform:capitalize}:host(.admin-mode) .le-slot-header-no-label{justify-content:flex-end;height:16px;border:none;background-color:transparent}.le-slot-label{color:var(--le-slot-label-color);text-align:start;overflow:hidden;width:0;flex:1 1 0%}.le-slot-required{color:var(--le-slot-required-color);font-weight:bold}:host(.admin-mode) .le-slot-description{display:block;padding:4px 8px;font-size:12px;color:var(--le-slot-description-color);font-style:italic}:host(.admin-mode) .le-slot-description-icon{display:inline-block;font-size:9px;line-height:1;cursor:pointer;color:var(--le-slot-description-color)}:host(.admin-mode) .le-slot-dropzone{display:block;min-height:var(--le-slot-dropzone-min-height);padding:var(--le-spacing-1, 4px);position:relative}:host(.admin-mode) .le-slot-dropzone:empty::before{content:'Drop content here';display:flex;align-items:center;justify-content:center;position:absolute;inset:8px;border:2px dashed var(--le-slot-dropzone-border-color);border-radius:4px;color:#999;font-size:12px;pointer-events:none}:host(.admin-mode.drag-over) .le-slot-container{border-color:#00cc66;background:rgba(0, 204, 102, 0.1)}:host(.admin-mode.drag-over) .le-slot-dropzone:empty::before{border-color:#00cc66;color:#00cc66;content:'Release to drop'}:host(.admin-mode) .le-slot-input{display:block;padding:var(--le-spacing-1, 4px)}:host(.admin-mode) .le-slot-input input,:host(.admin-mode) .le-slot-input textarea{display:block;width:100%;padding:8px 10px;border:1px solid var(--le-slot-dropzone-border-color);border-radius:4px;font-family:inherit;font-size:14px;line-height:1.4;background:#fff;color:#333;box-sizing:border-box;transition:border-color 0.2s, box-shadow 0.2s}:host(.admin-mode) .le-slot-input input:focus,:host(.admin-mode) .le-slot-input textarea:focus{outline:none;border-color:var(--le-slot-border-color);box-shadow:0 0 0 3px rgba(0, 136, 255, 0.15)}:host(.admin-mode) .le-slot-input input::placeholder,:host(.admin-mode) .le-slot-input textarea::placeholder{color:#999}:host(.admin-mode) .le-slot-input textarea{resize:vertical;min-height:60px}:host(.admin-mode) .le-slot-input slot{display:none}.le-slot-invalid{color:var(--le-slot-required-color);font-size:10px;margin-left:auto;font-weight:normal;text-transform:none}:host(.admin-mode) .le-slot-input.has-error input,:host(.admin-mode) .le-slot-input.has-error textarea{border-color:var(--le-slot-required-color);background:rgba(229, 57, 53, 0.05)}:host(.admin-mode) .le-slot-input.has-error input:focus,:host(.admin-mode) .le-slot-input.has-error textarea:focus{border-color:var(--le-slot-required-color);box-shadow:0 0 0 3px rgba(229, 57, 53, 0.15)}.le-slot-add-btn{font-size:24px;line-height:0px;width:12px;height:12px}.le-slot-header-no-label .le-slot-add-btn{font-size:16px}.le-slot-button{width:20px;height:20px}:host(.admin-mode) .le-slot-header-no-label.le-slot-header-text{height:0}";
|
|
726
|
+
|
|
727
|
+
const LeSlot = class {
|
|
728
|
+
constructor(hostRef) {
|
|
729
|
+
index.registerInstance(this, hostRef);
|
|
730
|
+
this.leSlotChange = index.createEvent(this, "leSlotChange");
|
|
731
|
+
}
|
|
732
|
+
get el() { return index.getElement(this); }
|
|
733
|
+
/**
|
|
734
|
+
* The type of slot content.
|
|
735
|
+
* - `slot`: Default, shows a dropzone for components (default)
|
|
736
|
+
* - `text`: Shows a single-line text input
|
|
737
|
+
* - `textarea`: Shows a multi-line text area
|
|
738
|
+
*/
|
|
739
|
+
type = 'slot';
|
|
740
|
+
/**
|
|
741
|
+
* The name of the slot this placeholder represents.
|
|
742
|
+
* Should match the slot name in the parent component.
|
|
743
|
+
*/
|
|
744
|
+
name = '';
|
|
745
|
+
/**
|
|
746
|
+
* Label to display in admin mode.
|
|
747
|
+
* If not provided, the slot name will be used.
|
|
748
|
+
*/
|
|
749
|
+
label;
|
|
750
|
+
/**
|
|
751
|
+
* Description of what content this slot accepts.
|
|
752
|
+
* Shown in admin mode to guide content editors.
|
|
753
|
+
*/
|
|
754
|
+
description;
|
|
755
|
+
/**
|
|
756
|
+
* Comma-separated list of allowed component tags for this slot.
|
|
757
|
+
* Used by CMS to filter available components.
|
|
758
|
+
*
|
|
759
|
+
* @example "le-card,le-button,le-text"
|
|
760
|
+
*/
|
|
761
|
+
allowedComponents;
|
|
762
|
+
/**
|
|
763
|
+
* Whether multiple components can be dropped in this slot.
|
|
764
|
+
*/
|
|
765
|
+
multiple = true;
|
|
766
|
+
/**
|
|
767
|
+
* Whether this slot is required to have content.
|
|
768
|
+
*/
|
|
769
|
+
required = false;
|
|
770
|
+
/**
|
|
771
|
+
* Placeholder text for text/textarea inputs in admin mode.
|
|
772
|
+
*/
|
|
773
|
+
placeholder;
|
|
774
|
+
/**
|
|
775
|
+
* The HTML tag to create when there's no slotted element.
|
|
776
|
+
* Used with type="text" or type="textarea" to auto-create elements.
|
|
777
|
+
*
|
|
778
|
+
* @example "h3" - creates <h3 slot="header">content</h3>
|
|
779
|
+
* @example "p" - creates <p slot="content">content</p>
|
|
780
|
+
*/
|
|
781
|
+
tag;
|
|
782
|
+
/**
|
|
783
|
+
* CSS styles for the slot dropzone container.
|
|
784
|
+
* Useful for layouts - e.g., "flex-direction: row" for horizontal stacks.
|
|
785
|
+
* Only applies in admin mode for type="slot".
|
|
786
|
+
*/
|
|
787
|
+
slotStyle;
|
|
788
|
+
/**
|
|
789
|
+
* Internal state to track admin mode
|
|
790
|
+
*/
|
|
791
|
+
adminMode = false;
|
|
792
|
+
/**
|
|
793
|
+
* Internal state for text input value (synced from slot content)
|
|
794
|
+
*/
|
|
795
|
+
textValue = '';
|
|
796
|
+
/**
|
|
797
|
+
* Whether the current textValue contains valid HTML
|
|
798
|
+
*/
|
|
799
|
+
isValidHtml = true;
|
|
800
|
+
/**
|
|
801
|
+
* Available components loaded from Custom Elements Manifest
|
|
802
|
+
*/
|
|
803
|
+
availableComponents = [];
|
|
804
|
+
/**
|
|
805
|
+
* Whether the component picker popover is open
|
|
806
|
+
*/
|
|
807
|
+
pickerOpen = false;
|
|
808
|
+
/**
|
|
809
|
+
* Reference to the slot element to access assignedNodes
|
|
810
|
+
*/
|
|
811
|
+
slotRef;
|
|
812
|
+
/**
|
|
813
|
+
* The original slotted element (e.g., <h3 slot="header">)
|
|
814
|
+
*/
|
|
815
|
+
slottedElement;
|
|
816
|
+
/**
|
|
817
|
+
* Emitted when text content changes in admin mode.
|
|
818
|
+
* The event detail contains the new text value and validity.
|
|
819
|
+
*/
|
|
820
|
+
leSlotChange;
|
|
821
|
+
disconnectModeObserver;
|
|
822
|
+
connectedCallback() {
|
|
823
|
+
this.disconnectModeObserver = utils.observeModeChanges(this.el, mode => {
|
|
824
|
+
const wasAdmin = this.adminMode;
|
|
825
|
+
this.adminMode = mode === 'admin';
|
|
826
|
+
// When entering admin mode, read content from slotted elements
|
|
827
|
+
if (this.adminMode && !wasAdmin) {
|
|
828
|
+
// Need to wait for render to access slot ref
|
|
829
|
+
requestAnimationFrame(() => this.readSlottedContent());
|
|
830
|
+
// Load available components for the component picker
|
|
831
|
+
if (this.type === 'slot') {
|
|
832
|
+
this.loadAvailableComponents();
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
disconnectedCallback() {
|
|
838
|
+
this.disconnectModeObserver?.();
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Flag to prevent re-reading content right after we updated it
|
|
842
|
+
*/
|
|
843
|
+
isUpdating = false;
|
|
844
|
+
/**
|
|
845
|
+
* Read content from slotted elements via assignedNodes()
|
|
846
|
+
*/
|
|
847
|
+
readSlottedContent() {
|
|
848
|
+
if (!this.slotRef)
|
|
849
|
+
return;
|
|
850
|
+
// Skip if we just updated the content ourselves
|
|
851
|
+
if (this.isUpdating) {
|
|
852
|
+
this.isUpdating = false;
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const assignedNodes = this.slotRef.assignedNodes({ flatten: true });
|
|
856
|
+
// For text/textarea types, we want to edit the innerHTML of slotted elements
|
|
857
|
+
if (this.type === 'text' || this.type === 'textarea') {
|
|
858
|
+
// Find the first element node (skip text nodes that are just whitespace)
|
|
859
|
+
const elementNode = assignedNodes.find(node => node.nodeType === Node.ELEMENT_NODE);
|
|
860
|
+
if (elementNode) {
|
|
861
|
+
// Only update textValue if slotted element changed or we don't have one yet
|
|
862
|
+
if (this.slottedElement !== elementNode) {
|
|
863
|
+
this.slottedElement = elementNode;
|
|
864
|
+
this.textValue = elementNode.innerHTML?.trim() || '';
|
|
865
|
+
// console.log(`[le-slot "${this.name}"] Read slotted content:`, this.textValue);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
// No element, check for direct text content
|
|
870
|
+
const textContent = assignedNodes
|
|
871
|
+
.filter(node => node.nodeType === Node.TEXT_NODE)
|
|
872
|
+
.map(node => node.textContent)
|
|
873
|
+
.join('')
|
|
874
|
+
.trim();
|
|
875
|
+
if (textContent && !this.textValue) {
|
|
876
|
+
this.textValue = textContent;
|
|
877
|
+
// console.log(`[le-slot "${this.name}"] Read text content:`, this.textValue);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Validates if a string contains valid HTML
|
|
884
|
+
*/
|
|
885
|
+
validateHtml(html) {
|
|
886
|
+
// Empty string is valid
|
|
887
|
+
if (!html.trim())
|
|
888
|
+
return true;
|
|
889
|
+
// Create a template element to parse the HTML
|
|
890
|
+
const template = document.createElement('template');
|
|
891
|
+
template.innerHTML = html;
|
|
892
|
+
// Check that we don't have obviously broken HTML
|
|
893
|
+
// Count opening and closing tags for common elements
|
|
894
|
+
const openTags = (html.match(/<[a-z][^>]*(?<!\/)>/gi) || []).length;
|
|
895
|
+
const closeTags = (html.match(/<\/[a-z][^>]*>/gi) || []).length;
|
|
896
|
+
const selfClosing = (html.match(/<[a-z][^>]*\/>/gi) || []).length;
|
|
897
|
+
// Simple validation: opening tags (minus self-closing) should roughly match closing tags
|
|
898
|
+
// Allow some tolerance for void elements like <br>, <img>, etc.
|
|
899
|
+
const voidElements = (html.match(/<(br|hr|img|input|meta|link|area|base|col|embed|param|source|track|wbr)[^>]*>/gi) || []).length;
|
|
900
|
+
const effectiveOpenTags = openTags - selfClosing - voidElements;
|
|
901
|
+
// If difference is too large, HTML is likely broken
|
|
902
|
+
if (Math.abs(effectiveOpenTags - closeTags) > 1) {
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
return true;
|
|
906
|
+
}
|
|
907
|
+
handleTextInput = (event) => {
|
|
908
|
+
const target = event.target;
|
|
909
|
+
this.textValue = target.value;
|
|
910
|
+
this.isValidHtml = this.validateHtml(this.textValue);
|
|
911
|
+
if (this.isValidHtml) {
|
|
912
|
+
// Set flag to prevent slotchange from re-reading what we just wrote
|
|
913
|
+
this.isUpdating = true;
|
|
914
|
+
console.log('Updating text value:', this.textValue, 'slottedElement:', this.slottedElement);
|
|
915
|
+
if (this.slottedElement) {
|
|
916
|
+
// Update existing slotted element's innerHTML
|
|
917
|
+
this.slottedElement.innerHTML = this.textValue;
|
|
918
|
+
}
|
|
919
|
+
else if (this.tag && this.textValue) {
|
|
920
|
+
// No slotted element exists
|
|
921
|
+
// If the slot doesn't have a name, then it's the default slot
|
|
922
|
+
// remove the existing non-slotted content (text nodes and elements without slot attribute)
|
|
923
|
+
const rootNode = this.el.getRootNode();
|
|
924
|
+
if (!this.name && rootNode instanceof ShadowRoot) {
|
|
925
|
+
const hostComponent = rootNode.host;
|
|
926
|
+
Array.from(hostComponent.childNodes).forEach(node => {
|
|
927
|
+
if (node.nodeType === Node.TEXT_NODE || (node.nodeType === Node.ELEMENT_NODE && !node.hasAttribute('slot'))) {
|
|
928
|
+
node.remove();
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
// create one using the specified tag
|
|
933
|
+
this.createSlottedElement();
|
|
934
|
+
}
|
|
935
|
+
else if (this.textValue) {
|
|
936
|
+
// no tag specified - just replace everything in the host component
|
|
937
|
+
const rootNode = this.el.getRootNode();
|
|
938
|
+
if (rootNode instanceof ShadowRoot) {
|
|
939
|
+
const hostComponent = rootNode.host;
|
|
940
|
+
hostComponent.innerHTML = this.textValue;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
this.leSlotChange.emit({
|
|
945
|
+
name: this.name,
|
|
946
|
+
value: this.textValue,
|
|
947
|
+
isValid: this.isValidHtml,
|
|
948
|
+
});
|
|
949
|
+
};
|
|
950
|
+
/**
|
|
951
|
+
* Create a new slotted element when none exists.
|
|
952
|
+
* The element is appended to the host component's light DOM.
|
|
953
|
+
*/
|
|
954
|
+
createSlottedElement() {
|
|
955
|
+
if (!this.tag)
|
|
956
|
+
return;
|
|
957
|
+
// Find the host component (le-card, etc.) by traversing up through shadow DOM
|
|
958
|
+
// le-slot is inside le-card's shadow DOM, so we need to find le-card's host
|
|
959
|
+
const rootNode = this.el.getRootNode();
|
|
960
|
+
if (!(rootNode instanceof ShadowRoot))
|
|
961
|
+
return;
|
|
962
|
+
const hostComponent = rootNode.host;
|
|
963
|
+
if (!hostComponent)
|
|
964
|
+
return;
|
|
965
|
+
// Create the new element
|
|
966
|
+
const newElement = document.createElement(this.tag);
|
|
967
|
+
newElement.innerHTML = this.textValue;
|
|
968
|
+
// Set the slot attribute if this is a named slot
|
|
969
|
+
if (this.name) {
|
|
970
|
+
newElement.setAttribute('slot', this.name);
|
|
971
|
+
}
|
|
972
|
+
// Append to the host component's light DOM
|
|
973
|
+
hostComponent.appendChild(newElement);
|
|
974
|
+
// Store reference to the new element
|
|
975
|
+
this.slottedElement = newElement;
|
|
976
|
+
// console.log(`[le-slot "${this.name}"] Created new <${this.tag}> element`);
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Load available components from Custom Elements Manifest
|
|
980
|
+
*/
|
|
981
|
+
async loadAvailableComponents() {
|
|
982
|
+
try {
|
|
983
|
+
const { manifestUrl } = index.getLeKitConfig();
|
|
984
|
+
const response = await fetch(manifestUrl);
|
|
985
|
+
const manifest = await response.json();
|
|
986
|
+
const components = [];
|
|
987
|
+
const allowedList = this.allowedComponents?.split(',').map(s => s.trim()) || [];
|
|
988
|
+
for (const module of manifest.modules) {
|
|
989
|
+
for (const declaration of module.declarations || []) {
|
|
990
|
+
if (declaration.tagName && declaration.customElement) {
|
|
991
|
+
// Skip internal components (le-slot, le-component, le-popover)
|
|
992
|
+
const isInternal = ['le-slot', 'le-component', 'le-popover'].includes(declaration.tagName);
|
|
993
|
+
if (isInternal)
|
|
994
|
+
continue;
|
|
995
|
+
// If allowedComponents is specified, filter by it
|
|
996
|
+
if (allowedList.length > 0 && !allowedList.includes(declaration.tagName)) {
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
components.push({
|
|
1000
|
+
tagName: declaration.tagName,
|
|
1001
|
+
name: this.formatComponentName(declaration.tagName),
|
|
1002
|
+
description: declaration.description || '',
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
this.availableComponents = components || [];
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
console.warn('[le-slot] Failed to load component manifest:', error);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Format a tag name into a display name
|
|
1015
|
+
* e.g., 'le-card' -> 'Card'
|
|
1016
|
+
*/
|
|
1017
|
+
formatComponentName(tagName) {
|
|
1018
|
+
return tagName
|
|
1019
|
+
.replace(/^le-/, '')
|
|
1020
|
+
.split('-')
|
|
1021
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
1022
|
+
.join(' ');
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Add a new component to the slot
|
|
1026
|
+
*/
|
|
1027
|
+
addComponent(tagName) {
|
|
1028
|
+
// Find the host component by traversing up through shadow DOM
|
|
1029
|
+
const rootNode = this.el.getRootNode();
|
|
1030
|
+
if (!(rootNode instanceof ShadowRoot))
|
|
1031
|
+
return;
|
|
1032
|
+
const hostComponent = rootNode.host;
|
|
1033
|
+
if (!hostComponent)
|
|
1034
|
+
return;
|
|
1035
|
+
// Create the new component element
|
|
1036
|
+
const newElement = document.createElement(tagName);
|
|
1037
|
+
// Set the slot attribute if this is a named slot
|
|
1038
|
+
if (this.name) {
|
|
1039
|
+
newElement.setAttribute('slot', this.name);
|
|
1040
|
+
}
|
|
1041
|
+
// Append to the host component's light DOM
|
|
1042
|
+
hostComponent.appendChild(newElement);
|
|
1043
|
+
// Emit change event so the page can save
|
|
1044
|
+
this.leSlotChange.emit({
|
|
1045
|
+
name: this.name,
|
|
1046
|
+
value: hostComponent.innerHTML,
|
|
1047
|
+
isValid: true,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Handle slot change event to re-read content when nodes are assigned
|
|
1052
|
+
*/
|
|
1053
|
+
handleSlotChange = () => {
|
|
1054
|
+
this.readSlottedContent();
|
|
1055
|
+
};
|
|
1056
|
+
render() {
|
|
1057
|
+
const displayLabel = this.label || this.name;
|
|
1058
|
+
// Always render the same structure, CSS handles visibility via .admin-mode class
|
|
1059
|
+
return (index.h(index.Host, { key: '5ff8f0dd3c07f92e0584450fabf57b89ea0c0a10', class: {
|
|
1060
|
+
'admin-mode': this.adminMode,
|
|
1061
|
+
'invalid-html': !this.isValidHtml,
|
|
1062
|
+
}, role: this.adminMode ? 'region' : undefined, "aria-label": this.adminMode ? `Slot: ${displayLabel}` : undefined, "data-slot-name": this.name, "data-slot-type": this.type, "data-allowed": this.allowedComponents, "data-multiple": this.multiple, "data-required": this.required }, this.adminMode ? (index.h("div", { class: "le-slot-container" }, index.h("div", { class: utils.classnames('le-slot-header', {
|
|
1063
|
+
'le-slot-header-no-label': !displayLabel,
|
|
1064
|
+
'le-slot-header-text': this.type === 'text',
|
|
1065
|
+
'le-slot-header-error': !this.isValidHtml,
|
|
1066
|
+
}) }, displayLabel && (index.h("span", { class: "le-slot-label" }, displayLabel, this.required && index.h("span", { class: "le-slot-required" }, "*"))), !this.isValidHtml && index.h("span", { class: "le-slot-invalid" }, "\u26A0 Invalid HTML"), this.type === 'slot' && this.adminMode && (index.h("le-popover", { mode: "default", showClose: true, align: "start", position: "right", popoverTitle: "Add Component", open: this.pickerOpen, onLePopoverOpen: () => (this.pickerOpen = true), onLePopoverClose: () => (this.pickerOpen = false) }, index.h("le-button", { type: "button", class: "le-slot-button", slot: "trigger", variant: "clear", size: "small", "aria-label": "Add component", "icon-only": true }, index.h("span", { class: "le-slot-add-btn", slot: "icon-only" }, "+")), index.h("div", { class: "le-slot-picker" }, this.availableComponents.length > 0 ? (index.h("ul", { class: "le-slot-picker-list" }, this.availableComponents.map(component => (index.h("li", { key: component.tagName }, index.h("button", { class: "le-slot-picker-item", onClick: () => {
|
|
1067
|
+
this.addComponent(component.tagName);
|
|
1068
|
+
this.pickerOpen = false;
|
|
1069
|
+
} }, index.h("span", { class: "le-slot-picker-name" }, component.name), component.description && index.h("span", { class: "le-slot-picker-desc" }, component.description))))))) : (index.h("div", { class: "le-slot-picker-empty" }, "No components available")))))), this.renderContent())) : (
|
|
1070
|
+
// In default mode, just pass through the slot - slotted content renders naturally
|
|
1071
|
+
// Note: We use unnamed slot here because named slots from parent component
|
|
1072
|
+
// are passed as le-slot's light DOM children
|
|
1073
|
+
index.h("slot", null))));
|
|
1074
|
+
}
|
|
1075
|
+
renderContent() {
|
|
1076
|
+
// Create the slot element with ref for reading assignedNodes
|
|
1077
|
+
// Wrap in a hidden div since slot elements can't have style prop in Stencil
|
|
1078
|
+
// Note: We use unnamed slot here because named slots from parent component
|
|
1079
|
+
// are passed as le-slot's light DOM children
|
|
1080
|
+
const slotElement = (index.h("div", { class: "hidden-slot" }, index.h("slot", { ref: el => (this.slotRef = el), onSlotchange: this.handleSlotChange })));
|
|
1081
|
+
switch (this.type) {
|
|
1082
|
+
case 'text':
|
|
1083
|
+
return (index.h("div", { class: { 'le-slot-input': true, 'has-error': !this.isValidHtml } }, index.h("le-string-input", { mode: "default", value: this.textValue, placeholder: this.placeholder || `Enter ${this.label || this.name || 'text'}...`, onChange: this.handleTextInput }), slotElement));
|
|
1084
|
+
case 'textarea':
|
|
1085
|
+
return (index.h("div", { class: { 'le-slot-input': true, 'has-error': !this.isValidHtml } }, index.h("textarea", { value: this.textValue, placeholder: this.placeholder || `Enter ${this.label || this.name || 'text'}...`, onInput: this.handleTextInput, required: this.required, rows: 3 }), slotElement));
|
|
1086
|
+
case 'slot':
|
|
1087
|
+
default:
|
|
1088
|
+
// Parse slotStyle string into style object if provided
|
|
1089
|
+
const dropzoneStyle = {};
|
|
1090
|
+
if (this.slotStyle) {
|
|
1091
|
+
this.slotStyle.split(';').forEach(rule => {
|
|
1092
|
+
const [prop, value] = rule.split(':').map(s => s.trim());
|
|
1093
|
+
if (prop && value) {
|
|
1094
|
+
// Convert kebab-case to camelCase for style object
|
|
1095
|
+
const camelProp = prop.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1096
|
+
dropzoneStyle[camelProp] = value;
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
return (index.h("div", { class: "le-slot-dropzone", style: dropzoneStyle }, index.h("slot", { ref: el => (this.slotRef = el), onSlotchange: this.handleSlotChange })));
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
LeSlot.style = leSlotDefaultCss;
|
|
1105
|
+
|
|
1106
|
+
const leStringInputCss = ":host{display:block;--le-input-bg:var(--le-color-surface, #ffffff);--le-input-color:var(--le-color-text-primary, #333333);--le-input-border:var(--le-border-width, 2px) solid var(--le-color-border-input, #007bff);--le-input-radius:var(--le-radius-sm, 4px);--le-input-padding:2px 6px;--le-input-height:1.5rem;--le-input-label-color:var(--le-color-text-primary, #333333);--le-input-desc-color:var(--le-color-text-secondary, #666666);--le-input-placeholder-color:#999999}.le-input-wrapper{display:flex;flex-direction:column;gap:2px}.le-input-label{display:block;font-size:0.9em;font-weight:500;color:var(--le-input-label-color);margin-bottom:2px}.le-input-container{position:relative;display:flex;align-items:center;background:var(--le-input-bg);border:var(--le-input-border);border-radius:var(--le-input-radius);transition:border-color 0.2s}.le-input-container:focus-within{outline:2px solid var(--le-color-focus);outline-offset:2px}:host([disabled]) .le-input-container{opacity:0.6;background-color:rgba(0,0,0,0.05);cursor:not-allowed}input{flex:1;min-height:var(--le-input-height);padding:var(--le-input-padding);border:none;background:transparent;color:var(--le-input-color);font-family:inherit;font-size:inherit;outline:none;width:100%}input::placeholder{color:var(--le-input-placeholder-color)}.icon-start,.icon-end{display:flex;align-items:center;justify-content:center;padding:0 8px;color:var(--le-input-desc-color)}.le-input-description{font-size:0.85em;color:var(--le-input-desc-color);margin-top:2px}.le-input-description::has(le-slot>slot[name=description]:empty){display:none}";
|
|
1107
|
+
|
|
1108
|
+
const LeStringInput = class {
|
|
1109
|
+
constructor(hostRef) {
|
|
1110
|
+
index.registerInstance(this, hostRef);
|
|
1111
|
+
this.leChange = index.createEvent(this, "change");
|
|
1112
|
+
this.leInput = index.createEvent(this, "input");
|
|
1113
|
+
}
|
|
1114
|
+
get el() { return index.getElement(this); }
|
|
1115
|
+
/**
|
|
1116
|
+
* Mode of the popover should be 'default' for internal use
|
|
1117
|
+
*/
|
|
1118
|
+
mode;
|
|
1119
|
+
/**
|
|
1120
|
+
* The value of the input
|
|
1121
|
+
*/
|
|
1122
|
+
value;
|
|
1123
|
+
/**
|
|
1124
|
+
* The name of the input
|
|
1125
|
+
*/
|
|
1126
|
+
name;
|
|
1127
|
+
/**
|
|
1128
|
+
* The type of the input (text, email, password, etc.)
|
|
1129
|
+
*/
|
|
1130
|
+
type = 'text';
|
|
1131
|
+
/**
|
|
1132
|
+
* Label for the input
|
|
1133
|
+
*/
|
|
1134
|
+
label;
|
|
1135
|
+
/**
|
|
1136
|
+
* Icon for the start icon
|
|
1137
|
+
*/
|
|
1138
|
+
iconStart;
|
|
1139
|
+
/**
|
|
1140
|
+
* Icon for the end icon
|
|
1141
|
+
*/
|
|
1142
|
+
iconEnd;
|
|
1143
|
+
/**
|
|
1144
|
+
* Placeholder text
|
|
1145
|
+
*/
|
|
1146
|
+
placeholder;
|
|
1147
|
+
/**
|
|
1148
|
+
* Whether the input is disabled
|
|
1149
|
+
*/
|
|
1150
|
+
disabled = false;
|
|
1151
|
+
/**
|
|
1152
|
+
* Whether the input is read-only
|
|
1153
|
+
*/
|
|
1154
|
+
readonly = false;
|
|
1155
|
+
/**
|
|
1156
|
+
* External ID for linking with external systems
|
|
1157
|
+
*/
|
|
1158
|
+
externalId;
|
|
1159
|
+
/**
|
|
1160
|
+
* Emitted when the value changes (on blur or Enter)
|
|
1161
|
+
*/
|
|
1162
|
+
leChange;
|
|
1163
|
+
/**
|
|
1164
|
+
* Emitted when the input value changes (on keystroke)
|
|
1165
|
+
*/
|
|
1166
|
+
leInput;
|
|
1167
|
+
handleInput = (ev) => {
|
|
1168
|
+
const input = ev.target;
|
|
1169
|
+
this.value = input.value;
|
|
1170
|
+
this.leInput.emit({
|
|
1171
|
+
value: this.value,
|
|
1172
|
+
name: this.name,
|
|
1173
|
+
externalId: this.externalId
|
|
1174
|
+
});
|
|
1175
|
+
};
|
|
1176
|
+
handleChange = (ev) => {
|
|
1177
|
+
const input = ev.target;
|
|
1178
|
+
this.value = input.value;
|
|
1179
|
+
this.leChange.emit({
|
|
1180
|
+
value: this.value,
|
|
1181
|
+
name: this.name,
|
|
1182
|
+
externalId: this.externalId
|
|
1183
|
+
});
|
|
1184
|
+
};
|
|
1185
|
+
handleClick = (ev) => {
|
|
1186
|
+
ev.stopPropagation();
|
|
1187
|
+
};
|
|
1188
|
+
render() {
|
|
1189
|
+
return (index.h("le-component", { key: 'd0c69370dae2d1fee5700954e4823d2a03a51331', component: "le-string-input", hostClass: utils.classnames({ 'disabled': this.disabled }) }, index.h("div", { key: '4acae8d3c34da2a86970a616c493ff210d561f5f', class: "le-input-wrapper" }, this.label && (index.h("label", { key: '609191b45187b6b1a65d05cd594b149760ac6882', class: "le-input-label", htmlFor: this.name }, this.label)), index.h("div", { key: '36b4caff4468ac7421db03f811cb3ef4a622b001', class: "le-input-container" }, this.iconStart && (index.h("span", { key: '344f88887fe8270bbef7e26ec1ad5da9fae1f8e4', class: "icon-start" }, this.iconStart)), index.h("input", { key: '4ba7beeddd7fb3cf23d03e029d11a804764cdd6e', id: this.name, type: this.type, name: this.name, value: this.value, placeholder: this.placeholder, disabled: this.disabled, readOnly: this.readonly, onInput: this.handleInput, onChange: this.handleChange, onClick: this.handleClick }), this.iconEnd && (index.h("span", { key: '7cdd4b52c3e1a1b18b19e697bdb42431941bba01', class: "icon-end" }, this.iconEnd))), index.h("div", { key: '113a75aa413e4d95300aeaa97d1ce7a75cf68c7a', class: "le-input-description" }, index.h("le-slot", { key: '0b37fc14e6df68f6c44cf9001d63a70f019e1cc3', name: "description", type: "text", tag: "p", label: "Description" }, index.h("slot", { key: '2674056dc915fabdb4fcbcaa13294a116b9509a6', name: "description" }))))));
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
LeStringInput.style = leStringInputCss;
|
|
1193
|
+
|
|
1194
|
+
exports.le_button = LeButton;
|
|
1195
|
+
exports.le_checkbox = LeCheckbox;
|
|
1196
|
+
exports.le_component = LeComponent;
|
|
1197
|
+
exports.le_popover = LePopover;
|
|
1198
|
+
exports.le_slot = LeSlot;
|
|
1199
|
+
exports.le_string_input = LeStringInput;
|
|
1200
|
+
//# sourceMappingURL=le-button.le-checkbox.le-component.le-popover.le-slot.le-string-input.entry.cjs.js.map
|