@rettangoli/ui 0.1.2-rc9 → 0.1.4
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/rettangoli-iife-layout.min.js +128 -56
- package/dist/rettangoli-iife-ui.min.js +187 -66
- package/package.json +3 -2
- package/src/common.js +30 -2
- package/src/components/breadcrumb/breadcrumb.handlers.js +10 -0
- package/src/components/breadcrumb/breadcrumb.store.js +29 -0
- package/src/components/breadcrumb/breadcrumb.view.yaml +64 -0
- package/src/components/dropdownMenu/dropdownMenu.handlers.js +7 -6
- package/src/components/dropdownMenu/dropdownMenu.store.js +6 -18
- package/src/components/dropdownMenu/dropdownMenu.view.yaml +15 -13
- package/src/components/form/form.handlers.js +181 -33
- package/src/components/form/form.store.js +175 -21
- package/src/components/form/form.view.yaml +182 -27
- package/src/components/globalUi/globalUi.handlers.js +53 -0
- package/src/components/globalUi/globalUi.store.js +57 -0
- package/src/components/globalUi/globalUi.view.yaml +50 -0
- package/src/components/navbar/navbar.handlers.js +2 -1
- package/src/components/navbar/navbar.store.js +2 -2
- package/src/components/pageOutline/pageOutline.handlers.js +57 -17
- package/src/components/pageOutline/pageOutline.store.js +48 -3
- package/src/components/pageOutline/pageOutline.view.yaml +7 -5
- package/src/components/popoverInput/popoverInput.handlers.js +103 -0
- package/src/components/popoverInput/popoverInput.store.js +48 -0
- package/src/components/popoverInput/popoverInput.view.yaml +55 -0
- package/src/components/select/select.handlers.js +124 -12
- package/src/components/select/select.store.js +86 -20
- package/src/components/select/select.view.yaml +40 -10
- package/src/components/sidebar/sidebar.handlers.js +8 -6
- package/src/components/sidebar/sidebar.store.js +6 -6
- package/src/components/sidebar/sidebar.view.yaml +1 -1
- package/src/components/sliderInput/sliderInput.handlers.js +24 -6
- package/src/components/sliderInput/sliderInput.store.js +3 -2
- package/src/components/sliderInput/sliderInput.view.yaml +3 -2
- package/src/components/table/table.handlers.js +12 -10
- package/src/components/table/table.store.js +4 -4
- package/src/components/tabs/tabs.handlers.js +11 -0
- package/src/components/tabs/tabs.store.js +29 -0
- package/src/components/tabs/tabs.view.yaml +64 -0
- package/src/components/tooltip/tooltip.handlers.js +0 -0
- package/src/components/tooltip/tooltip.store.js +12 -0
- package/src/components/tooltip/tooltip.view.yaml +27 -0
- package/src/components/waveform/waveform.handlers.js +92 -0
- package/src/components/waveform/waveform.store.js +17 -0
- package/src/components/waveform/waveform.view.yaml +38 -0
- package/src/deps/createGlobalUI.js +39 -0
- package/src/entry-iife-layout.js +3 -0
- package/src/entry-iife-ui.js +4 -0
- package/src/index.js +7 -1
- package/src/primitives/button.js +10 -0
- package/src/primitives/colorPicker.js +13 -4
- package/src/primitives/dialog.js +254 -0
- package/src/primitives/image.js +4 -3
- package/src/primitives/input.js +17 -4
- package/src/primitives/popover.js +280 -0
- package/src/primitives/slider.js +14 -4
- package/src/primitives/svg.js +2 -0
- package/src/primitives/textarea.js +25 -1
- package/src/primitives/view.js +132 -13
- package/src/setup.js +7 -2
- package/src/styles/cursorStyles.js +38 -2
- package/src/components/dialog/dialog.handlers.js +0 -5
- package/src/components/dialog/dialog.store.js +0 -25
- package/src/components/dialog/dialog.view.yaml +0 -48
- package/src/components/popover/popover.handlers.js +0 -5
- package/src/components/popover/popover.store.js +0 -12
- package/src/components/popover/popover.view.yaml +0 -57
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export const handleAfterMount = async (deps) => {
|
|
2
|
+
const { props, store, render, getRefIds, } = deps;
|
|
3
|
+
const { waveformData } = props;
|
|
4
|
+
|
|
5
|
+
store.setWaveformData(waveformData);
|
|
6
|
+
render();
|
|
7
|
+
|
|
8
|
+
const canvas = getRefIds().canvas?.elm;
|
|
9
|
+
if (canvas) {
|
|
10
|
+
renderWaveform(waveformData, canvas);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const handleOnUpdate = async (deps, payload) => {
|
|
15
|
+
const { store, render, getRefIds, props } = deps;
|
|
16
|
+
const { waveformData } = props;
|
|
17
|
+
|
|
18
|
+
if (!waveformData) {
|
|
19
|
+
console.log('waveform handleOnUpdate: no waveformData provided');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
store.setWaveformData(waveformData);
|
|
24
|
+
render();
|
|
25
|
+
|
|
26
|
+
const canvas = getRefIds().canvas?.elm;
|
|
27
|
+
if (canvas) {
|
|
28
|
+
renderWaveform(waveformData, canvas);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function renderWaveform(waveformData, canvas) {
|
|
33
|
+
const ctx = canvas.getContext("2d");
|
|
34
|
+
|
|
35
|
+
// Get the actual display size of the canvas
|
|
36
|
+
const rect = canvas.getBoundingClientRect();
|
|
37
|
+
const displayWidth = rect.width;
|
|
38
|
+
const displayHeight = rect.height;
|
|
39
|
+
|
|
40
|
+
// Set canvas internal resolution to match display size
|
|
41
|
+
canvas.width = displayWidth;
|
|
42
|
+
canvas.height = displayHeight;
|
|
43
|
+
|
|
44
|
+
const width = canvas.width;
|
|
45
|
+
const height = canvas.height;
|
|
46
|
+
|
|
47
|
+
// Clear canvas
|
|
48
|
+
ctx.clearRect(0, 0, width, height);
|
|
49
|
+
|
|
50
|
+
// Dark theme background
|
|
51
|
+
ctx.fillStyle = "#1a1a1a";
|
|
52
|
+
ctx.fillRect(0, 0, width, height);
|
|
53
|
+
|
|
54
|
+
if (!waveformData || !waveformData.amplitudes) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const amplitudes = waveformData.amplitudes;
|
|
59
|
+
const centerY = height / 2;
|
|
60
|
+
|
|
61
|
+
// Create gradient for waveform
|
|
62
|
+
const gradient = ctx.createLinearGradient(0, 0, 0, height);
|
|
63
|
+
gradient.addColorStop(0, "#404040");
|
|
64
|
+
gradient.addColorStop(0.5, "#A1A1A1");
|
|
65
|
+
gradient.addColorStop(1, "#404040");
|
|
66
|
+
|
|
67
|
+
// Draw waveform bars
|
|
68
|
+
const barWidth = Math.max(1, width / amplitudes.length);
|
|
69
|
+
const barSpacing = 0.2; // 20% spacing between bars
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < amplitudes.length; i++) {
|
|
72
|
+
const amplitude = amplitudes[i];
|
|
73
|
+
const barHeight = amplitude * (height * 0.85);
|
|
74
|
+
const x = i * barWidth;
|
|
75
|
+
const y = centerY - barHeight / 2;
|
|
76
|
+
|
|
77
|
+
ctx.fillStyle = gradient;
|
|
78
|
+
ctx.fillRect(x, y, Math.max(1, barWidth * (1 - barSpacing)), barHeight);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Draw subtle center line
|
|
82
|
+
ctx.strokeStyle = "rgba(255, 255, 255, 0.1)";
|
|
83
|
+
ctx.lineWidth = 1;
|
|
84
|
+
ctx.beginPath();
|
|
85
|
+
ctx.moveTo(0, centerY);
|
|
86
|
+
ctx.lineTo(width, centerY);
|
|
87
|
+
ctx.stroke();
|
|
88
|
+
|
|
89
|
+
// Add subtle glow effect
|
|
90
|
+
ctx.shadowBlur = 10;
|
|
91
|
+
ctx.shadowColor = "#2196F3";
|
|
92
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const createInitialState = () => Object.freeze({
|
|
2
|
+
waveformData: null,
|
|
3
|
+
});
|
|
4
|
+
|
|
5
|
+
export const setWaveformData = (state, data) => {
|
|
6
|
+
state.waveformData = data;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const selectViewData = ({ state, attrs, props }) => {
|
|
10
|
+
return {
|
|
11
|
+
isLoading: props.isLoading,
|
|
12
|
+
w: attrs.w || "250",
|
|
13
|
+
h: attrs.h || "150",
|
|
14
|
+
cur: attrs.cur,
|
|
15
|
+
waveformData: props.waveformData,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
elementName: rtgl-waveform
|
|
2
|
+
|
|
3
|
+
attrsSchema:
|
|
4
|
+
type: object
|
|
5
|
+
properties:
|
|
6
|
+
w:
|
|
7
|
+
type: string
|
|
8
|
+
description: Width of the waveform visualizer
|
|
9
|
+
default: '250'
|
|
10
|
+
h:
|
|
11
|
+
type: string
|
|
12
|
+
description: Height of the waveform visualizer
|
|
13
|
+
default: '150'
|
|
14
|
+
cur:
|
|
15
|
+
type: string
|
|
16
|
+
description: cursor
|
|
17
|
+
|
|
18
|
+
propsSchema:
|
|
19
|
+
type: object
|
|
20
|
+
properties:
|
|
21
|
+
waveformData:
|
|
22
|
+
type: object
|
|
23
|
+
description: File ID of the waveform data in object storage
|
|
24
|
+
isLoading:
|
|
25
|
+
type: boolean
|
|
26
|
+
description: Whether the waveform data is currently being loaded
|
|
27
|
+
|
|
28
|
+
refs:
|
|
29
|
+
canvas:
|
|
30
|
+
selector: canvas
|
|
31
|
+
|
|
32
|
+
template:
|
|
33
|
+
- rtgl-view w=f h=f pos=rel w=${w} h=${h} cur=${cur}:
|
|
34
|
+
- $if isLoading:
|
|
35
|
+
- rtgl-view w=f h=f av=c ah=c:
|
|
36
|
+
- rtgl-text c=mu-fg: ...
|
|
37
|
+
$else:
|
|
38
|
+
- 'canvas#canvas style="width:100%; height:100%;"':
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const createGlobalUI = (globalUIElement) => {
|
|
2
|
+
let listeners = {};
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
once: (event, callback) => {
|
|
6
|
+
if (!listeners[event]) {
|
|
7
|
+
listeners[event] = [];
|
|
8
|
+
}
|
|
9
|
+
const onceCallback = (...args) => {
|
|
10
|
+
callback(...args);
|
|
11
|
+
listeners[event] = listeners[event].filter(cb => cb !== onceCallback);
|
|
12
|
+
}
|
|
13
|
+
listeners[event].push(onceCallback);
|
|
14
|
+
},
|
|
15
|
+
emit: (event, ...args) => {
|
|
16
|
+
if (listeners[event]) {
|
|
17
|
+
listeners[event].forEach(callback => {
|
|
18
|
+
callback(...args);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
showAlert: async (options) => {
|
|
23
|
+
if(!globalUIElement)
|
|
24
|
+
{
|
|
25
|
+
throw new Error("globalUIElement is not set. Make sure to initialize the global UI component and pass it to createGlobalUIManager.");
|
|
26
|
+
}
|
|
27
|
+
globalUIElement.transformedHandlers.showAlert(options);
|
|
28
|
+
},
|
|
29
|
+
showConfirm: async (options) => {
|
|
30
|
+
if(!globalUIElement)
|
|
31
|
+
{
|
|
32
|
+
throw new Error("globalUIElement is not set. Make sure to initialize the global UI component and pass it to createGlobalUIManager.");
|
|
33
|
+
}
|
|
34
|
+
return globalUIElement.transformedHandlers.showConfirm(options);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default createGlobalUI;
|
package/src/entry-iife-layout.js
CHANGED
|
@@ -5,6 +5,8 @@ import RettangoliImage from './primitives/image.js';
|
|
|
5
5
|
import RettangoliSvg from './primitives/svg.js';
|
|
6
6
|
import RettangoliInput from './primitives/input.js';
|
|
7
7
|
import RettangoliTextArea from './primitives/textarea.js';
|
|
8
|
+
import RettangoliDialog from './primitives/dialog.js';
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
customElements.define("rtgl-button", RettangoliButton({}));
|
|
10
12
|
customElements.define("rtgl-view", RettangoliView({}));
|
|
@@ -13,3 +15,4 @@ customElements.define("rtgl-image", RettangoliImage({}));
|
|
|
13
15
|
customElements.define("rtgl-svg", RettangoliSvg({}));
|
|
14
16
|
customElements.define("rtgl-input", RettangoliInput({}));
|
|
15
17
|
customElements.define("rtgl-textarea", RettangoliTextArea({}));
|
|
18
|
+
customElements.define("rtgl-dialog", RettangoliDialog({}));
|
package/src/entry-iife-ui.js
CHANGED
|
@@ -7,6 +7,8 @@ import RettangoliInput from './primitives/input.js';
|
|
|
7
7
|
import RettangoliTextArea from './primitives/textarea.js';
|
|
8
8
|
import RettangoliColorPicker from './primitives/colorPicker.js';
|
|
9
9
|
import RettangoliSlider from './primitives/slider.js';
|
|
10
|
+
import RettangoliDialog from './primitives/dialog.js';
|
|
11
|
+
import RettangoliPopover from './primitives/popover.js';
|
|
10
12
|
|
|
11
13
|
customElements.define("rtgl-button", RettangoliButton({}));
|
|
12
14
|
customElements.define("rtgl-view", RettangoliView({}));
|
|
@@ -17,6 +19,8 @@ customElements.define("rtgl-input", RettangoliInput({}));
|
|
|
17
19
|
customElements.define("rtgl-textarea", RettangoliTextArea({}));
|
|
18
20
|
customElements.define("rtgl-color-picker", RettangoliColorPicker({}));
|
|
19
21
|
customElements.define("rtgl-slider", RettangoliSlider({}));
|
|
22
|
+
customElements.define("rtgl-dialog", RettangoliDialog({}));
|
|
23
|
+
customElements.define("rtgl-popover", RettangoliPopover({}));
|
|
20
24
|
|
|
21
25
|
// built from rettangoli cli fe
|
|
22
26
|
import '../.temp/dynamicImport.js'
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,9 @@ import RettangoliImage from './primitives/image.js';
|
|
|
5
5
|
import RettangoliSvg from './primitives/svg.js';
|
|
6
6
|
import RettangoliInput from './primitives/input.js';
|
|
7
7
|
import RettangoliTextArea from './primitives/textarea.js';
|
|
8
|
+
import RettangoliDialog from './primitives/dialog.js';
|
|
9
|
+
import RettangoliPopover from './primitives/popover.js';
|
|
10
|
+
import createGlobalUI from './deps/createGlobalUI.js';
|
|
8
11
|
|
|
9
12
|
export {
|
|
10
13
|
RettangoliButton,
|
|
@@ -14,4 +17,7 @@ export {
|
|
|
14
17
|
RettangoliSvg,
|
|
15
18
|
RettangoliInput,
|
|
16
19
|
RettangoliTextArea,
|
|
17
|
-
|
|
20
|
+
RettangoliDialog,
|
|
21
|
+
RettangoliPopover,
|
|
22
|
+
createGlobalUI,
|
|
23
|
+
}
|
package/src/primitives/button.js
CHANGED
|
@@ -296,6 +296,16 @@ class RettangoliButtonElement extends HTMLElement {
|
|
|
296
296
|
this._buttonElement.style.maxWidth = "";
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
|
+
|
|
300
|
+
// Public method to get the actual button's bounding rect
|
|
301
|
+
// This is needed because the host element has display: contents
|
|
302
|
+
getBoundingClientRect() {
|
|
303
|
+
if (this._buttonElement) {
|
|
304
|
+
return this._buttonElement.getBoundingClientRect();
|
|
305
|
+
}
|
|
306
|
+
// Fallback to host element
|
|
307
|
+
return super.getBoundingClientRect();
|
|
308
|
+
}
|
|
299
309
|
}
|
|
300
310
|
|
|
301
311
|
// Export factory function to maintain API compatibility
|
|
@@ -81,8 +81,8 @@ class RettangoliColorPickerElement extends HTMLElement {
|
|
|
81
81
|
"wh",
|
|
82
82
|
"w",
|
|
83
83
|
"h",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
84
|
+
"hide",
|
|
85
|
+
"show",
|
|
86
86
|
"op",
|
|
87
87
|
"z",
|
|
88
88
|
])
|
|
@@ -114,6 +114,15 @@ class RettangoliColorPickerElement extends HTMLElement {
|
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
117
|
+
// Handle key attribute change - reset value
|
|
118
|
+
if (name === "key" && oldValue !== newValue) {
|
|
119
|
+
requestAnimationFrame(() => {
|
|
120
|
+
const value = this.getAttribute("value");
|
|
121
|
+
this._inputElement.value = value ?? "#000000";
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
117
126
|
// Handle input-specific attributes first
|
|
118
127
|
if (["value", "disabled"].includes(name)) {
|
|
119
128
|
this._updateInputAttributes();
|
|
@@ -168,11 +177,11 @@ class RettangoliColorPickerElement extends HTMLElement {
|
|
|
168
177
|
this._styles[size]["max-height"] = height;
|
|
169
178
|
}
|
|
170
179
|
|
|
171
|
-
if (this.hasAttribute(addSizePrefix("
|
|
180
|
+
if (this.hasAttribute(addSizePrefix("hide"))) {
|
|
172
181
|
this._styles[size].display = "none !important";
|
|
173
182
|
}
|
|
174
183
|
|
|
175
|
-
if (this.hasAttribute(addSizePrefix("
|
|
184
|
+
if (this.hasAttribute(addSizePrefix("show"))) {
|
|
176
185
|
this._styles[size].display = "block !important";
|
|
177
186
|
}
|
|
178
187
|
});
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { css } from "../common.js";
|
|
2
|
+
|
|
3
|
+
class RettangoliDialogElement extends HTMLElement {
|
|
4
|
+
static styleSheet = null;
|
|
5
|
+
|
|
6
|
+
static initializeStyleSheet() {
|
|
7
|
+
if (!RettangoliDialogElement.styleSheet) {
|
|
8
|
+
RettangoliDialogElement.styleSheet = new CSSStyleSheet();
|
|
9
|
+
RettangoliDialogElement.styleSheet.replaceSync(css`
|
|
10
|
+
:host {
|
|
11
|
+
display: contents;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
dialog {
|
|
15
|
+
padding: 0;
|
|
16
|
+
border: none;
|
|
17
|
+
background: transparent;
|
|
18
|
+
margin: auto;
|
|
19
|
+
overflow-y: scroll;
|
|
20
|
+
color: inherit;
|
|
21
|
+
max-height: 100vh;
|
|
22
|
+
height: 100vh;
|
|
23
|
+
max-width: 100vw;
|
|
24
|
+
scrollbar-width: none;
|
|
25
|
+
outline: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
dialog::backdrop {
|
|
29
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
slot[name="content"] {
|
|
33
|
+
background-color: var(--background) !important;
|
|
34
|
+
display: block;
|
|
35
|
+
padding: var(--spacing-lg);
|
|
36
|
+
border: 1px solid var(--border);
|
|
37
|
+
border-radius: var(--border-radius-md);
|
|
38
|
+
margin-left: var(--spacing-lg);
|
|
39
|
+
margin-right: var(--spacing-lg);
|
|
40
|
+
width: fit-content;
|
|
41
|
+
max-width: calc(100vw - 2 * var(--spacing-lg));
|
|
42
|
+
/* Default margins will be set dynamically via JavaScript for adaptive centering */
|
|
43
|
+
margin-top: 40px;
|
|
44
|
+
margin-bottom: 40px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Size attribute styles */
|
|
48
|
+
:host([s="sm"]) slot[name="content"] {
|
|
49
|
+
width: 33vw;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:host([s="md"]) slot[name="content"] {
|
|
53
|
+
width: 50vw;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:host([s="lg"]) slot[name="content"] {
|
|
57
|
+
width: 80vw;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:host([s="f"]) slot[name="content"] {
|
|
61
|
+
width: 100vw;
|
|
62
|
+
margin-left: 0;
|
|
63
|
+
margin-right: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@keyframes dialog-in {
|
|
67
|
+
from {
|
|
68
|
+
opacity: 0;
|
|
69
|
+
transform: scale(0.95);
|
|
70
|
+
}
|
|
71
|
+
to {
|
|
72
|
+
opacity: 1;
|
|
73
|
+
transform: scale(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dialog[open] slot[name="content"] {
|
|
78
|
+
animation: dialog-in 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
79
|
+
}
|
|
80
|
+
`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
constructor() {
|
|
85
|
+
super();
|
|
86
|
+
RettangoliDialogElement.initializeStyleSheet();
|
|
87
|
+
this.shadow = this.attachShadow({ mode: "open" });
|
|
88
|
+
this.shadow.adoptedStyleSheets = [RettangoliDialogElement.styleSheet];
|
|
89
|
+
|
|
90
|
+
// Create dialog element
|
|
91
|
+
this._dialogElement = document.createElement('dialog');
|
|
92
|
+
this.shadow.appendChild(this._dialogElement);
|
|
93
|
+
|
|
94
|
+
// Store reference for content slot
|
|
95
|
+
this._slotElement = null;
|
|
96
|
+
this._isConnected = false;
|
|
97
|
+
|
|
98
|
+
// Handle click outside - emit custom event
|
|
99
|
+
this._dialogElement.addEventListener('click', (e) => {
|
|
100
|
+
if (e.target === this._dialogElement) {
|
|
101
|
+
this.dispatchEvent(new CustomEvent('close', {
|
|
102
|
+
detail: {}
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Handle right-click on overlay to close dialog
|
|
108
|
+
this._dialogElement.addEventListener('contextmenu', (e) => {
|
|
109
|
+
if (e.target === this._dialogElement) {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
this.dispatchEvent(new CustomEvent('close', {
|
|
112
|
+
detail: {}
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Handle ESC key - prevent native close and emit custom event
|
|
118
|
+
this._dialogElement.addEventListener('cancel', (e) => {
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
this.dispatchEvent(new CustomEvent('close', {
|
|
121
|
+
detail: {}
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static get observedAttributes() {
|
|
127
|
+
return ["open", "w", "s"];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
connectedCallback() {
|
|
131
|
+
this._updateDialog();
|
|
132
|
+
this._isConnected = true;
|
|
133
|
+
// Check initial open attribute
|
|
134
|
+
if (this.hasAttribute('open')) {
|
|
135
|
+
this._showModal();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
140
|
+
if (name === 'open') {
|
|
141
|
+
if (newValue !== null && !this._dialogElement.open && this._isConnected) {
|
|
142
|
+
this._showModal();
|
|
143
|
+
} else if (newValue === null && this._dialogElement.open) {
|
|
144
|
+
this._hideModal();
|
|
145
|
+
}
|
|
146
|
+
} else if (name === 'w') {
|
|
147
|
+
this._updateWidth();
|
|
148
|
+
} else if (name === 's') {
|
|
149
|
+
// Size is handled via CSS :host() selectors
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_updateDialog() {
|
|
154
|
+
this._updateWidth();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_updateWidth() {
|
|
158
|
+
const width = this.getAttribute('w');
|
|
159
|
+
if (width) {
|
|
160
|
+
this._dialogElement.style.width = width;
|
|
161
|
+
} else {
|
|
162
|
+
this._dialogElement.style.width = '';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Internal methods
|
|
167
|
+
_showModal() {
|
|
168
|
+
if (!this._dialogElement.open) {
|
|
169
|
+
// Create and append slot for content only if it doesn't exist
|
|
170
|
+
if (!this._slotElement) {
|
|
171
|
+
this._slotElement = document.createElement('slot');
|
|
172
|
+
this._slotElement.setAttribute('name', 'content');
|
|
173
|
+
this._dialogElement.appendChild(this._slotElement);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this._dialogElement.showModal();
|
|
177
|
+
|
|
178
|
+
// Reset scroll position
|
|
179
|
+
this._dialogElement.scrollTop = 0;
|
|
180
|
+
|
|
181
|
+
// Apply adaptive centering
|
|
182
|
+
this._applyAdaptiveCentering();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
_hideModal() {
|
|
187
|
+
if (this._dialogElement.open) {
|
|
188
|
+
this._dialogElement.close();
|
|
189
|
+
|
|
190
|
+
// Remove slot to unmount content
|
|
191
|
+
if (this._slotElement) {
|
|
192
|
+
// Reset any inline styles applied for adaptive centering
|
|
193
|
+
this._slotElement.style.marginTop = '';
|
|
194
|
+
this._slotElement.style.marginBottom = '';
|
|
195
|
+
|
|
196
|
+
this._dialogElement.removeChild(this._slotElement);
|
|
197
|
+
this._slotElement = null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Reset dialog height
|
|
201
|
+
this._dialogElement.style.height = '';
|
|
202
|
+
|
|
203
|
+
// Don't emit any event when programmatically closed via attribute
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
_applyAdaptiveCentering() {
|
|
208
|
+
if (!this._slotElement) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Use requestAnimationFrame to ensure DOM has updated
|
|
213
|
+
requestAnimationFrame(() => {
|
|
214
|
+
if (!this._slotElement) return;
|
|
215
|
+
|
|
216
|
+
// Get the actual height of the content
|
|
217
|
+
const contentHeight = this._slotElement.offsetHeight;
|
|
218
|
+
const viewportHeight = window.innerHeight;
|
|
219
|
+
|
|
220
|
+
// Calculate centered position with minimum margins for scrollability
|
|
221
|
+
const minMargin = 40; // Minimum margin in pixels to ensure scrollability
|
|
222
|
+
|
|
223
|
+
if (contentHeight >= viewportHeight - (2 * minMargin)) {
|
|
224
|
+
// Content is too tall, use minimum margins to allow scrolling
|
|
225
|
+
// Start near the top with small margin so content isn't pushed too far down
|
|
226
|
+
this._slotElement.style.marginTop = `${minMargin}px`;
|
|
227
|
+
this._slotElement.style.marginBottom = `${minMargin}px`;
|
|
228
|
+
// Keep dialog at full height for scrolling
|
|
229
|
+
this._dialogElement.style.height = '100vh';
|
|
230
|
+
} else {
|
|
231
|
+
// Content fits, center it vertically
|
|
232
|
+
const totalMargin = viewportHeight - contentHeight;
|
|
233
|
+
const margin = Math.floor(totalMargin / 2);
|
|
234
|
+
this._slotElement.style.marginTop = `${margin}px`;
|
|
235
|
+
this._slotElement.style.marginBottom = `${margin}px`;
|
|
236
|
+
// Set dialog height to auto to prevent unnecessary scrollbar
|
|
237
|
+
this._dialogElement.style.height = 'auto';
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
// Expose dialog element for advanced usage
|
|
244
|
+
get dialog() {
|
|
245
|
+
return this._dialogElement;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Export factory function to maintain API compatibility
|
|
250
|
+
export default ({ render, html }) => {
|
|
251
|
+
// Note: render and html parameters are accepted but not used
|
|
252
|
+
// This maintains backward compatibility with existing code
|
|
253
|
+
return RettangoliDialogElement;
|
|
254
|
+
};
|
package/src/primitives/image.js
CHANGED
|
@@ -85,7 +85,8 @@ class RettangoliImageElement extends HTMLElement {
|
|
|
85
85
|
"wh",
|
|
86
86
|
"w",
|
|
87
87
|
"h",
|
|
88
|
-
"
|
|
88
|
+
"hide",
|
|
89
|
+
"show",
|
|
89
90
|
"height",
|
|
90
91
|
"width",
|
|
91
92
|
"z",
|
|
@@ -198,11 +199,11 @@ class RettangoliImageElement extends HTMLElement {
|
|
|
198
199
|
this._styles[size]["max-height"] = height;
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
if (this.hasAttribute(addSizePrefix("
|
|
202
|
+
if (this.hasAttribute(addSizePrefix("hide"))) {
|
|
202
203
|
this._styles[size].display = "none !important";
|
|
203
204
|
}
|
|
204
205
|
|
|
205
|
-
if (this.hasAttribute(addSizePrefix("
|
|
206
|
+
if (this.hasAttribute(addSizePrefix("show"))) {
|
|
206
207
|
this._styles[size].display = "block !important";
|
|
207
208
|
}
|
|
208
209
|
});
|
package/src/primitives/input.js
CHANGED
|
@@ -94,8 +94,8 @@ class RettangoliInputElement extends HTMLElement {
|
|
|
94
94
|
"wh",
|
|
95
95
|
"w",
|
|
96
96
|
"h",
|
|
97
|
-
"
|
|
98
|
-
"
|
|
97
|
+
"hide",
|
|
98
|
+
"show",
|
|
99
99
|
"op",
|
|
100
100
|
"z",
|
|
101
101
|
])
|
|
@@ -110,6 +110,10 @@ class RettangoliInputElement extends HTMLElement {
|
|
|
110
110
|
this._inputElement.value = newValue;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
focus() {
|
|
114
|
+
this._inputElement.focus();
|
|
115
|
+
}
|
|
116
|
+
|
|
113
117
|
_onChange = (event) => {
|
|
114
118
|
this.dispatchEvent(new CustomEvent('input-change', {
|
|
115
119
|
detail: {
|
|
@@ -119,6 +123,15 @@ class RettangoliInputElement extends HTMLElement {
|
|
|
119
123
|
};
|
|
120
124
|
|
|
121
125
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
126
|
+
// Handle key attribute change - reset value
|
|
127
|
+
if (name === "key" && oldValue !== newValue) {
|
|
128
|
+
requestAnimationFrame((() => {
|
|
129
|
+
const value = this.getAttribute("value");
|
|
130
|
+
this._inputElement.value = value ?? "";
|
|
131
|
+
}))
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
122
135
|
// Handle input-specific attributes first
|
|
123
136
|
if (["type", "placeholder", "disabled", "value", "step", "s"].includes(name)) {
|
|
124
137
|
this._updateInputAttributes();
|
|
@@ -173,11 +186,11 @@ class RettangoliInputElement extends HTMLElement {
|
|
|
173
186
|
this._styles[size]["max-height"] = height;
|
|
174
187
|
}
|
|
175
188
|
|
|
176
|
-
if (this.hasAttribute(addSizePrefix("
|
|
189
|
+
if (this.hasAttribute(addSizePrefix("hide"))) {
|
|
177
190
|
this._styles[size].display = "none !important";
|
|
178
191
|
}
|
|
179
192
|
|
|
180
|
-
if (this.hasAttribute(addSizePrefix("
|
|
193
|
+
if (this.hasAttribute(addSizePrefix("show"))) {
|
|
181
194
|
this._styles[size].display = "block !important";
|
|
182
195
|
}
|
|
183
196
|
});
|