@rettangoli/ui 0.1.2-rc2 → 0.1.2-rc21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/rettangoli-iife-layout.min.js +102 -32
  2. package/dist/rettangoli-iife-ui.min.js +182 -69
  3. package/package.json +5 -3
  4. package/src/cli/buildSvg.js +86 -0
  5. package/src/cli/index.js +1 -0
  6. package/src/components/breadcrumb/breadcrumb.handlers.js +9 -0
  7. package/src/components/breadcrumb/breadcrumb.store.js +29 -0
  8. package/src/components/breadcrumb/breadcrumb.view.yaml +64 -0
  9. package/src/components/dropdownMenu/dropdownMenu.handlers.js +4 -4
  10. package/src/components/dropdownMenu/dropdownMenu.store.js +5 -17
  11. package/src/components/dropdownMenu/dropdownMenu.view.yaml +15 -13
  12. package/src/components/form/form.handlers.js +133 -24
  13. package/src/components/form/form.store.js +125 -23
  14. package/src/components/form/form.view.yaml +139 -29
  15. package/src/components/pageOutline/pageOutline.handlers.js +1 -1
  16. package/src/components/popoverInput/popoverInput.handlers.js +99 -0
  17. package/src/components/popoverInput/popoverInput.store.js +48 -0
  18. package/src/components/popoverInput/popoverInput.view.yaml +55 -0
  19. package/src/components/select/select.handlers.js +2 -5
  20. package/src/components/select/select.view.yaml +3 -3
  21. package/src/components/sidebar/sidebar.view.yaml +1 -1
  22. package/src/components/sliderInput/sliderInput.handlers.js +24 -0
  23. package/src/components/sliderInput/sliderInput.store.js +17 -0
  24. package/src/components/sliderInput/sliderInput.view.yaml +42 -0
  25. package/src/components/table/table.handlers.js +1 -1
  26. package/src/components/tabs/tabs.handlers.js +10 -0
  27. package/src/components/tabs/tabs.store.js +29 -0
  28. package/src/components/tabs/tabs.view.yaml +64 -0
  29. package/src/components/waveform/waveform.handlers.js +92 -0
  30. package/src/components/waveform/waveform.store.js +17 -0
  31. package/src/components/waveform/waveform.view.yaml +38 -0
  32. package/src/entry-iife-layout.js +3 -0
  33. package/src/entry-iife-ui.js +4 -0
  34. package/src/index.js +5 -1
  35. package/src/primitives/dialog.js +208 -0
  36. package/src/primitives/input.js +32 -11
  37. package/src/primitives/popover.js +275 -0
  38. package/src/primitives/slider.js +8 -9
  39. package/src/styles/viewStyles.js +1 -0
  40. package/src/components/dialog/dialog.handlers.js +0 -5
  41. package/src/components/dialog/dialog.store.js +0 -25
  42. package/src/components/dialog/dialog.view.yaml +0 -44
  43. package/src/components/popover/popover.handlers.js +0 -5
  44. package/src/components/popover/popover.store.js +0 -12
  45. package/src/components/popover/popover.view.yaml +0 -57
@@ -0,0 +1,64 @@
1
+ elementName: rtgl-tabs
2
+
3
+ viewDataSchema:
4
+ type: object
5
+ properties:
6
+ containerAttrString:
7
+ type: string
8
+ items:
9
+ type: array
10
+ items:
11
+ type: object
12
+ properties:
13
+ label:
14
+ type: string
15
+ id:
16
+ type: string
17
+ isSelected:
18
+ type: boolean
19
+ bgColor:
20
+ type: string
21
+ textColor:
22
+ type: string
23
+ borderColor:
24
+ type: string
25
+ selectedTab:
26
+ type: string
27
+
28
+ propsSchema:
29
+ type: object
30
+ properties:
31
+ items:
32
+ type: array
33
+ items:
34
+ type: object
35
+ properties:
36
+ label:
37
+ type: string
38
+ id:
39
+ type: string
40
+
41
+ attrsSchema:
42
+ type: object
43
+ properties:
44
+ selected-tab:
45
+ type: string
46
+
47
+ refs:
48
+ tab-*:
49
+ eventListeners:
50
+ click:
51
+ handler: handleClickItem
52
+
53
+ events:
54
+ item-click:
55
+ type: object
56
+ properties:
57
+ id:
58
+ type: string
59
+
60
+ template:
61
+ - rtgl-view d=h g=sm bgc=mu p=sm br=lg ${containerAttrString}:
62
+ - $for item in items:
63
+ - rtgl-view#tab-${item.id} data-id=${item.id} cur=p bgc=${item.bgColor} bw=xs bc=${item.borderColor} pv=md ph=lg br=lg:
64
+ - rtgl-text s=sm c=${item.textColor}: "${item.label}"
@@ -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 (changes, deps) => {
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.data) {
55
+ return;
56
+ }
57
+
58
+ const data = waveformData.data;
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 / data.length);
69
+ const barSpacing = 0.2; // 20% spacing between bars
70
+
71
+ for (let i = 0; i < data.length; i++) {
72
+ const amplitude = data[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 INITIAL_STATE = Object.freeze({
2
+ waveformData: null,
3
+ });
4
+
5
+ export const setWaveformData = (state, data) => {
6
+ state.waveformData = data;
7
+ };
8
+
9
+ export const toViewData = ({ 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%;"':
@@ -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({}));
@@ -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,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
+ import RettangoliPopover from './primitives/popover.js';
8
10
 
9
11
  export {
10
12
  RettangoliButton,
@@ -14,4 +16,6 @@ export {
14
16
  RettangoliSvg,
15
17
  RettangoliInput,
16
18
  RettangoliTextArea,
17
- }
19
+ RettangoliDialog,
20
+ RettangoliPopover,
21
+ }
@@ -0,0 +1,208 @@
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-top: 20vh;
39
+ margin-bottom: 20vh;
40
+ margin-left: var(--spacing-lg);
41
+ margin-right: var(--spacing-lg);
42
+ width: fit-content;
43
+ max-width: calc(100vw - 2 * var(--spacing-lg));
44
+ }
45
+
46
+ /* Size attribute styles */
47
+ :host([s="sm"]) slot[name="content"] {
48
+ width: 33vw;
49
+ }
50
+
51
+ :host([s="md"]) slot[name="content"] {
52
+ width: 50vw;
53
+ }
54
+
55
+ :host([s="lg"]) slot[name="content"] {
56
+ width: 80vw;
57
+ }
58
+
59
+ :host([s="f"]) slot[name="content"] {
60
+ width: 100vw;
61
+ margin-left: 0;
62
+ margin-right: 0;
63
+ }
64
+
65
+ @keyframes dialog-in {
66
+ from {
67
+ opacity: 0;
68
+ transform: scale(0.95);
69
+ }
70
+ to {
71
+ opacity: 1;
72
+ transform: scale(1);
73
+ }
74
+ }
75
+
76
+ dialog[open] slot[name="content"] {
77
+ animation: dialog-in 150ms cubic-bezier(0.16, 1, 0.3, 1);
78
+ }
79
+ `);
80
+ }
81
+ }
82
+
83
+ constructor() {
84
+ super();
85
+ RettangoliDialogElement.initializeStyleSheet();
86
+ this.shadow = this.attachShadow({ mode: "open" });
87
+ this.shadow.adoptedStyleSheets = [RettangoliDialogElement.styleSheet];
88
+
89
+ // Create dialog element
90
+ this._dialogElement = document.createElement('dialog');
91
+ this.shadow.appendChild(this._dialogElement);
92
+
93
+ // Store reference for content slot
94
+ this._slotElement = null;
95
+ this._isConnected = false;
96
+
97
+ // Handle click outside - emit custom event
98
+ this._dialogElement.addEventListener('click', (e) => {
99
+ if (e.target === this._dialogElement) {
100
+ this.dispatchEvent(new CustomEvent('close', {
101
+ detail: {}
102
+ }));
103
+ }
104
+ });
105
+
106
+ // Handle right-click on overlay to close dialog
107
+ this._dialogElement.addEventListener('contextmenu', (e) => {
108
+ if (e.target === this._dialogElement) {
109
+ e.preventDefault();
110
+ this.dispatchEvent(new CustomEvent('close', {
111
+ detail: {}
112
+ }));
113
+ }
114
+ });
115
+
116
+ // Handle ESC key - prevent native close and emit custom event
117
+ this._dialogElement.addEventListener('cancel', (e) => {
118
+ e.preventDefault();
119
+ this.dispatchEvent(new CustomEvent('close', {
120
+ detail: {}
121
+ }));
122
+ });
123
+ }
124
+
125
+ static get observedAttributes() {
126
+ return ["open", "w", "s"];
127
+ }
128
+
129
+ connectedCallback() {
130
+ this._updateDialog();
131
+ this._isConnected = true;
132
+ // Check initial open attribute
133
+ if (this.hasAttribute('open')) {
134
+ this._showModal();
135
+ }
136
+ }
137
+
138
+ attributeChangedCallback(name, oldValue, newValue) {
139
+ if (name === 'open') {
140
+ if (newValue !== null && !this._dialogElement.open && this._isConnected) {
141
+ this._showModal();
142
+ } else if (newValue === null && this._dialogElement.open) {
143
+ this._hideModal();
144
+ }
145
+ } else if (name === 'w') {
146
+ this._updateWidth();
147
+ } else if (name === 's') {
148
+ // Size is handled via CSS :host() selectors
149
+ }
150
+ }
151
+
152
+ _updateDialog() {
153
+ this._updateWidth();
154
+ }
155
+
156
+ _updateWidth() {
157
+ const width = this.getAttribute('w');
158
+ if (width) {
159
+ this._dialogElement.style.width = width;
160
+ } else {
161
+ this._dialogElement.style.width = '';
162
+ }
163
+ }
164
+
165
+ // Internal methods
166
+ _showModal() {
167
+ if (!this._dialogElement.open) {
168
+ // Create and append slot for content only if it doesn't exist
169
+ if (!this._slotElement) {
170
+ this._slotElement = document.createElement('slot');
171
+ this._slotElement.setAttribute('name', 'content');
172
+ this._dialogElement.appendChild(this._slotElement);
173
+ }
174
+
175
+ this._dialogElement.showModal();
176
+
177
+ // Reset scroll position
178
+ this._dialogElement.scrollTop = 0;
179
+ }
180
+ }
181
+
182
+ _hideModal() {
183
+ if (this._dialogElement.open) {
184
+ this._dialogElement.close();
185
+
186
+ // Remove slot to unmount content
187
+ if (this._slotElement) {
188
+ this._dialogElement.removeChild(this._slotElement);
189
+ this._slotElement = null;
190
+ }
191
+
192
+ // Don't emit any event when programmatically closed via attribute
193
+ }
194
+ }
195
+
196
+
197
+ // Expose dialog element for advanced usage
198
+ get dialog() {
199
+ return this._dialogElement;
200
+ }
201
+ }
202
+
203
+ // Export factory function to maintain API compatibility
204
+ export default ({ render, html }) => {
205
+ // Note: render and html parameters are accepted but not used
206
+ // This maintains backward compatibility with existing code
207
+ return RettangoliDialogElement;
208
+ };
@@ -1,5 +1,5 @@
1
- import {
2
- css,
1
+ import {
2
+ css,
3
3
  dimensionWithUnit,
4
4
  convertObjectToCssString,
5
5
  styleMapKeys,
@@ -59,7 +59,7 @@ class RettangoliInputElement extends HTMLElement {
59
59
  RettangoliInputElement.initializeStyleSheet();
60
60
  this.shadow = this.attachShadow({ mode: "closed" });
61
61
  this.shadow.adoptedStyleSheets = [RettangoliInputElement.styleSheet];
62
-
62
+
63
63
  // Initialize style tracking properties
64
64
  this._styles = {
65
65
  default: {},
@@ -69,11 +69,11 @@ class RettangoliInputElement extends HTMLElement {
69
69
  xl: {},
70
70
  };
71
71
  this._lastStyleString = "";
72
-
72
+
73
73
  // Create initial DOM structure
74
74
  this._inputElement = document.createElement('input');
75
75
  this._styleElement = document.createElement('style');
76
-
76
+
77
77
  this.shadow.appendChild(this._styleElement);
78
78
  this.shadow.appendChild(this._inputElement);
79
79
 
@@ -83,10 +83,11 @@ class RettangoliInputElement extends HTMLElement {
83
83
 
84
84
  static get observedAttributes() {
85
85
  return [
86
- "key",
87
- "type",
88
- "placeholder",
86
+ "key",
87
+ "type",
88
+ "placeholder",
89
89
  "disabled",
90
+ "value",
90
91
  "s",
91
92
  ...permutateBreakpoints([
92
93
  ...styleMapKeys,
@@ -105,6 +106,14 @@ class RettangoliInputElement extends HTMLElement {
105
106
  return this._inputElement.value;
106
107
  }
107
108
 
109
+ set value(newValue) {
110
+ this._inputElement.value = newValue;
111
+ }
112
+
113
+ focus() {
114
+ this._inputElement.focus();
115
+ }
116
+
108
117
  _onChange = (event) => {
109
118
  this.dispatchEvent(new CustomEvent('input-change', {
110
119
  detail: {
@@ -115,7 +124,7 @@ class RettangoliInputElement extends HTMLElement {
115
124
 
116
125
  attributeChangedCallback(name, oldValue, newValue) {
117
126
  // Handle input-specific attributes first
118
- if (["type", "placeholder", "disabled", "s"].includes(name)) {
127
+ if (["type", "placeholder", "disabled", "value", "step", "s"].includes(name)) {
119
128
  this._updateInputAttributes();
120
129
  return;
121
130
  }
@@ -188,16 +197,28 @@ class RettangoliInputElement extends HTMLElement {
188
197
  _updateInputAttributes() {
189
198
  const type = this.getAttribute("type") || "text";
190
199
  const placeholder = this.getAttribute("placeholder");
200
+ const value = this.getAttribute("value");
201
+ const step = this.getAttribute("step");
191
202
  const isDisabled = this.hasAttribute('disabled');
192
203
 
193
204
  this._inputElement.setAttribute("type", type);
194
-
205
+
195
206
  if (placeholder !== null) {
196
207
  this._inputElement.setAttribute("placeholder", placeholder);
197
208
  } else {
198
209
  this._inputElement.removeAttribute("placeholder");
199
210
  }
200
-
211
+
212
+ if (value !== null) {
213
+ this._inputElement.value = value;
214
+ }
215
+
216
+ if (step !== null) {
217
+ this._inputElement.setAttribute("step", step);
218
+ } else {
219
+ this._inputElement.removeAttribute("step");
220
+ }
221
+
201
222
  if (isDisabled) {
202
223
  this._inputElement.setAttribute("disabled", "");
203
224
  } else {