juxscript 1.1.162 → 1.1.163

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/index.d.ts CHANGED
@@ -12,6 +12,7 @@ import { datepicker } from './lib/components/datepicker.js';
12
12
  import { dialog } from './lib/components/dialog.js';
13
13
  import { divider } from './lib/components/divider.js';
14
14
  import { dropdown } from './lib/components/dropdown.js';
15
+ import { dropdownMenu } from './lib/components/dropdown-menu.js';
15
16
  import { element } from './lib/components/element.js';
16
17
  import { fileupload } from './lib/components/fileupload.js';
17
18
  import { grid } from './lib/components/grid.js';
@@ -76,6 +77,7 @@ export declare const jux: {
76
77
  dialog: typeof dialog;
77
78
  divider: typeof divider;
78
79
  dropdown: typeof dropdown;
80
+ dropdownMenu: typeof dropdownMenu;
79
81
  element: typeof element;
80
82
  fileupload: typeof fileupload;
81
83
  grid: typeof grid;
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
package/index.js CHANGED
@@ -12,6 +12,7 @@ import { datepicker } from './lib/components/datepicker.js';
12
12
  import { dialog } from './lib/components/dialog.js';
13
13
  import { divider } from './lib/components/divider.js';
14
14
  import { dropdown } from './lib/components/dropdown.js';
15
+ import { dropdownMenu } from './lib/components/dropdown-menu.js';
15
16
  import { element } from './lib/components/element.js';
16
17
  import { fileupload } from './lib/components/fileupload.js';
17
18
  import { grid } from './lib/components/grid.js';
@@ -82,6 +83,7 @@ export const jux = {
82
83
  dialog,
83
84
  divider,
84
85
  dropdown,
86
+ dropdownMenu,
85
87
  element,
86
88
  fileupload,
87
89
  grid,
@@ -0,0 +1,42 @@
1
+ import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
+ export interface DropdownMenuItem {
3
+ type?: 'item' | 'label' | 'separator';
4
+ label?: string;
5
+ value?: string;
6
+ shortcut?: string;
7
+ disabled?: boolean;
8
+ submenu?: boolean;
9
+ }
10
+ export interface DropdownMenuOptions {
11
+ trigger?: string;
12
+ items?: DropdownMenuItem[];
13
+ style?: string;
14
+ class?: string;
15
+ }
16
+ type DropdownMenuState = BaseState & {
17
+ trigger: string;
18
+ items: DropdownMenuItem[];
19
+ open: boolean;
20
+ };
21
+ export declare class DropdownMenu extends BaseComponent<DropdownMenuState> {
22
+ private _wrapper;
23
+ private _menuEl;
24
+ constructor(id: string, options?: DropdownMenuOptions);
25
+ protected getTriggerEvents(): readonly string[];
26
+ protected getCallbackEvents(): readonly string[];
27
+ trigger(value: string): this;
28
+ items(value: DropdownMenuItem[]): this;
29
+ addItem(item: DropdownMenuItem): this;
30
+ separator(): this;
31
+ label(text: string): this;
32
+ item(label: string, value?: string, shortcut?: string): this;
33
+ open(): this;
34
+ close(): this;
35
+ toggle(): this;
36
+ update(prop: string, value: any): void;
37
+ render(targetId?: string | HTMLElement | BaseComponent<any>): this;
38
+ private _buildItems;
39
+ }
40
+ export declare function dropdownMenu(id: string, options?: DropdownMenuOptions): DropdownMenu;
41
+ export {};
42
+ //# sourceMappingURL=dropdown-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["dropdown-menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAKnE,MAAM,WAAW,gBAAgB;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,iBAAiB,GAAG,SAAS,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,qBAAa,YAAa,SAAQ,aAAa,CAAC,iBAAiB,CAAC;IAC9D,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,OAAO,CAA4B;gBAE/B,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB;IAUzD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAC/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAIhD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAE5B,KAAK,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,IAAI;IAEtC,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAKrC,SAAS,IAAI,IAAI;IAIjB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAI5D,IAAI,IAAI,IAAI;IAMZ,KAAK,IAAI,IAAI;IAMb,MAAM,IAAI,IAAI;IAId,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAItC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IA2ElE,OAAO,CAAC,WAAW;CA+CtB;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAExF"}
@@ -0,0 +1,177 @@
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+ const TRIGGER_EVENTS = [];
3
+ const CALLBACK_EVENTS = ['select'];
4
+ export class DropdownMenu extends BaseComponent {
5
+ constructor(id, options = {}) {
6
+ super(id, {
7
+ trigger: options.trigger ?? 'Open',
8
+ items: options.items ?? [],
9
+ open: false,
10
+ style: options.style ?? '',
11
+ class: options.class ?? ''
12
+ });
13
+ this._wrapper = null;
14
+ this._menuEl = null;
15
+ }
16
+ getTriggerEvents() { return TRIGGER_EVENTS; }
17
+ getCallbackEvents() { return CALLBACK_EVENTS; }
18
+ /* ═══════════════════════ FLUENT API ═══════════════════════ */
19
+ trigger(value) { this.state.trigger = value; return this; }
20
+ items(value) { this.state.items = value; return this; }
21
+ addItem(item) {
22
+ this.state.items = [...this.state.items, item];
23
+ return this;
24
+ }
25
+ separator() {
26
+ return this.addItem({ type: 'separator' });
27
+ }
28
+ label(text) {
29
+ return this.addItem({ type: 'label', label: text });
30
+ }
31
+ item(label, value, shortcut) {
32
+ return this.addItem({ label, value: value ?? label, shortcut });
33
+ }
34
+ open() {
35
+ this.state.open = true;
36
+ if (this._menuEl)
37
+ this._menuEl.style.display = 'flex';
38
+ return this;
39
+ }
40
+ close() {
41
+ this.state.open = false;
42
+ if (this._menuEl)
43
+ this._menuEl.style.display = 'none';
44
+ return this;
45
+ }
46
+ toggle() {
47
+ return this.state.open ? this.close() : this.open();
48
+ }
49
+ update(prop, value) { }
50
+ /* ═══════════════════════ RENDER ═══════════════════════ */
51
+ render(targetId) {
52
+ const container = this._setupContainer(targetId);
53
+ const { trigger, items, style, class: className } = this.state;
54
+ // Inject styles once
55
+ if (!document.getElementById('jux-dropdown-menu-styles')) {
56
+ const styleEl = document.createElement('style');
57
+ styleEl.id = 'jux-dropdown-menu-styles';
58
+ styleEl.textContent = `
59
+ .jux-dmenu-wrapper { position:relative; display:inline-block; }
60
+ .jux-dmenu-trigger { display:inline-flex; align-items:center; gap:4px; padding:8px 16px; font-size:14px; font-weight:500; border-radius:6px; border:1px solid hsl(var(--border,0 0% 80%)); background:hsl(var(--background,0 0% 100%)); color:hsl(var(--foreground,0 0% 9%)); cursor:pointer; transition:background .15s; }
61
+ .jux-dmenu-trigger:hover { background:hsl(var(--accent,0 0% 96%)); }
62
+ .jux-dmenu-menu { position:absolute; top:calc(100% + 4px); left:0; min-width:220px; padding:4px; border-radius:8px; border:1px solid hsl(var(--border,0 0% 90%)); background:hsl(var(--popover,0 0% 100%)); color:hsl(var(--popover-foreground,0 0% 9%)); box-shadow:0 4px 12px rgba(0,0,0,.08),0 1px 3px rgba(0,0,0,.06); z-index:100; display:none; flex-direction:column; animation:jux-dmenu-in .12s ease-out; }
63
+ @keyframes jux-dmenu-in { from{opacity:0;transform:translateY(-4px) scale(.98)} to{opacity:1;transform:translateY(0) scale(1)} }
64
+ .jux-dmenu-label { padding:6px 8px 4px; font-size:12px; font-weight:600; color:hsl(var(--muted-foreground,0 0% 45%)); user-select:none; }
65
+ .jux-dmenu-sep { height:1px; margin:4px -4px; background:hsl(var(--border,0 0% 90%)); }
66
+ .jux-dmenu-item { display:flex; align-items:center; justify-content:space-between; width:100%; padding:6px 8px; font-size:14px; border-radius:4px; border:none; background:transparent; color:hsl(var(--popover-foreground,0 0% 9%)); cursor:pointer; text-align:left; transition:background .1s; gap:8px; font-family:inherit; }
67
+ .jux-dmenu-item:hover { background:hsl(var(--accent,0 0% 96%)); }
68
+ .jux-dmenu-item:focus-visible { outline:2px solid hsl(var(--ring,0 0% 63%)); outline-offset:-2px; }
69
+ .jux-dmenu-item[disabled] { opacity:.5; pointer-events:none; }
70
+ .jux-dmenu-item-label { flex:1; }
71
+ .jux-dmenu-shortcut { font-size:12px; color:hsl(var(--muted-foreground,0 0% 45%)); letter-spacing:.04em; }
72
+ .jux-dmenu-arrow { font-size:10px; color:hsl(var(--muted-foreground,0 0% 45%)); }
73
+ `;
74
+ document.head.appendChild(styleEl);
75
+ }
76
+ const wrapper = document.createElement('div');
77
+ wrapper.className = 'jux-dmenu-wrapper';
78
+ wrapper.id = this._id;
79
+ if (className)
80
+ wrapper.className += ` ${className}`;
81
+ if (style)
82
+ wrapper.setAttribute('style', style);
83
+ const triggerBtn = document.createElement('button');
84
+ triggerBtn.className = 'jux-dmenu-trigger';
85
+ triggerBtn.textContent = trigger;
86
+ triggerBtn.type = 'button';
87
+ wrapper.appendChild(triggerBtn);
88
+ const menu = document.createElement('div');
89
+ menu.className = 'jux-dmenu-menu';
90
+ this._menuEl = menu;
91
+ this._buildItems(menu, items);
92
+ wrapper.appendChild(menu);
93
+ // Toggle
94
+ triggerBtn.addEventListener('click', (e) => { e.stopPropagation(); this.toggle(); });
95
+ document.addEventListener('click', (e) => { if (!wrapper.contains(e.target))
96
+ this.close(); });
97
+ // Keyboard
98
+ triggerBtn.addEventListener('keydown', (e) => {
99
+ if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
100
+ e.preventDefault();
101
+ this.open();
102
+ const first = menu.querySelector('.jux-dmenu-item:not([disabled])');
103
+ if (first)
104
+ first.focus();
105
+ }
106
+ });
107
+ menu.addEventListener('keydown', (e) => {
108
+ const btns = Array.from(menu.querySelectorAll('.jux-dmenu-item:not([disabled])'));
109
+ const idx = btns.indexOf(document.activeElement);
110
+ if (e.key === 'ArrowDown') {
111
+ e.preventDefault();
112
+ btns[(idx + 1) % btns.length]?.focus();
113
+ }
114
+ else if (e.key === 'ArrowUp') {
115
+ e.preventDefault();
116
+ btns[(idx - 1 + btns.length) % btns.length]?.focus();
117
+ }
118
+ else if (e.key === 'Escape') {
119
+ this.close();
120
+ triggerBtn.focus();
121
+ }
122
+ });
123
+ this._wireStandardEvents(wrapper);
124
+ container.appendChild(wrapper);
125
+ this._wrapper = wrapper;
126
+ return this;
127
+ }
128
+ _buildItems(menu, items) {
129
+ menu.innerHTML = '';
130
+ items.forEach(item => {
131
+ if (item.type === 'separator') {
132
+ const sep = document.createElement('div');
133
+ sep.className = 'jux-dmenu-sep';
134
+ menu.appendChild(sep);
135
+ }
136
+ else if (item.type === 'label') {
137
+ const lbl = document.createElement('div');
138
+ lbl.className = 'jux-dmenu-label';
139
+ lbl.textContent = item.label ?? '';
140
+ menu.appendChild(lbl);
141
+ }
142
+ else {
143
+ const btn = document.createElement('button');
144
+ btn.className = 'jux-dmenu-item';
145
+ btn.type = 'button';
146
+ if (item.disabled)
147
+ btn.disabled = true;
148
+ const labelSpan = document.createElement('span');
149
+ labelSpan.className = 'jux-dmenu-item-label';
150
+ labelSpan.textContent = item.label ?? '';
151
+ btn.appendChild(labelSpan);
152
+ if (item.submenu) {
153
+ const arrow = document.createElement('span');
154
+ arrow.className = 'jux-dmenu-arrow';
155
+ arrow.textContent = '›';
156
+ btn.appendChild(arrow);
157
+ }
158
+ else if (item.shortcut) {
159
+ const sc = document.createElement('span');
160
+ sc.className = 'jux-dmenu-shortcut';
161
+ sc.textContent = item.shortcut;
162
+ btn.appendChild(sc);
163
+ }
164
+ btn.addEventListener('click', (e) => {
165
+ if (!item.disabled) {
166
+ this._triggerCallback('select', item.value || item.label, e, this);
167
+ this.close();
168
+ }
169
+ });
170
+ menu.appendChild(btn);
171
+ }
172
+ });
173
+ }
174
+ }
175
+ export function dropdownMenu(id, options = {}) {
176
+ return new DropdownMenu(id, options);
177
+ }
@@ -0,0 +1,214 @@
1
+ import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
+
3
+ const TRIGGER_EVENTS = [] as const;
4
+ const CALLBACK_EVENTS = ['select'] as const;
5
+
6
+ export interface DropdownMenuItem {
7
+ type?: 'item' | 'label' | 'separator';
8
+ label?: string;
9
+ value?: string;
10
+ shortcut?: string;
11
+ disabled?: boolean;
12
+ submenu?: boolean;
13
+ }
14
+
15
+ export interface DropdownMenuOptions {
16
+ trigger?: string;
17
+ items?: DropdownMenuItem[];
18
+ style?: string;
19
+ class?: string;
20
+ }
21
+
22
+ type DropdownMenuState = BaseState & {
23
+ trigger: string;
24
+ items: DropdownMenuItem[];
25
+ open: boolean;
26
+ };
27
+
28
+ export class DropdownMenu extends BaseComponent<DropdownMenuState> {
29
+ private _wrapper: HTMLElement | null = null;
30
+ private _menuEl: HTMLElement | null = null;
31
+
32
+ constructor(id: string, options: DropdownMenuOptions = {}) {
33
+ super(id, {
34
+ trigger: options.trigger ?? 'Open',
35
+ items: options.items ?? [],
36
+ open: false,
37
+ style: options.style ?? '',
38
+ class: options.class ?? ''
39
+ });
40
+ }
41
+
42
+ protected getTriggerEvents(): readonly string[] { return TRIGGER_EVENTS; }
43
+ protected getCallbackEvents(): readonly string[] { return CALLBACK_EVENTS; }
44
+
45
+ /* ═══════════════════════ FLUENT API ═══════════════════════ */
46
+
47
+ trigger(value: string): this { this.state.trigger = value; return this; }
48
+
49
+ items(value: DropdownMenuItem[]): this { this.state.items = value; return this; }
50
+
51
+ addItem(item: DropdownMenuItem): this {
52
+ this.state.items = [...this.state.items, item];
53
+ return this;
54
+ }
55
+
56
+ separator(): this {
57
+ return this.addItem({ type: 'separator' });
58
+ }
59
+
60
+ label(text: string): this {
61
+ return this.addItem({ type: 'label', label: text });
62
+ }
63
+
64
+ item(label: string, value?: string, shortcut?: string): this {
65
+ return this.addItem({ label, value: value ?? label, shortcut });
66
+ }
67
+
68
+ open(): this {
69
+ this.state.open = true;
70
+ if (this._menuEl) this._menuEl.style.display = 'flex';
71
+ return this;
72
+ }
73
+
74
+ close(): this {
75
+ this.state.open = false;
76
+ if (this._menuEl) this._menuEl.style.display = 'none';
77
+ return this;
78
+ }
79
+
80
+ toggle(): this {
81
+ return this.state.open ? this.close() : this.open();
82
+ }
83
+
84
+ update(prop: string, value: any): void { }
85
+
86
+ /* ═══════════════════════ RENDER ═══════════════════════ */
87
+
88
+ render(targetId?: string | HTMLElement | BaseComponent<any>): this {
89
+ const container = this._setupContainer(targetId);
90
+ const { trigger, items, style, class: className } = this.state;
91
+
92
+ // Inject styles once
93
+ if (!document.getElementById('jux-dropdown-menu-styles')) {
94
+ const styleEl = document.createElement('style');
95
+ styleEl.id = 'jux-dropdown-menu-styles';
96
+ styleEl.textContent = `
97
+ .jux-dmenu-wrapper { position:relative; display:inline-block; }
98
+ .jux-dmenu-trigger { display:inline-flex; align-items:center; gap:4px; padding:8px 16px; font-size:14px; font-weight:500; border-radius:6px; border:1px solid hsl(var(--border,0 0% 80%)); background:hsl(var(--background,0 0% 100%)); color:hsl(var(--foreground,0 0% 9%)); cursor:pointer; transition:background .15s; }
99
+ .jux-dmenu-trigger:hover { background:hsl(var(--accent,0 0% 96%)); }
100
+ .jux-dmenu-menu { position:absolute; top:calc(100% + 4px); left:0; min-width:220px; padding:4px; border-radius:8px; border:1px solid hsl(var(--border,0 0% 90%)); background:hsl(var(--popover,0 0% 100%)); color:hsl(var(--popover-foreground,0 0% 9%)); box-shadow:0 4px 12px rgba(0,0,0,.08),0 1px 3px rgba(0,0,0,.06); z-index:100; display:none; flex-direction:column; animation:jux-dmenu-in .12s ease-out; }
101
+ @keyframes jux-dmenu-in { from{opacity:0;transform:translateY(-4px) scale(.98)} to{opacity:1;transform:translateY(0) scale(1)} }
102
+ .jux-dmenu-label { padding:6px 8px 4px; font-size:12px; font-weight:600; color:hsl(var(--muted-foreground,0 0% 45%)); user-select:none; }
103
+ .jux-dmenu-sep { height:1px; margin:4px -4px; background:hsl(var(--border,0 0% 90%)); }
104
+ .jux-dmenu-item { display:flex; align-items:center; justify-content:space-between; width:100%; padding:6px 8px; font-size:14px; border-radius:4px; border:none; background:transparent; color:hsl(var(--popover-foreground,0 0% 9%)); cursor:pointer; text-align:left; transition:background .1s; gap:8px; font-family:inherit; }
105
+ .jux-dmenu-item:hover { background:hsl(var(--accent,0 0% 96%)); }
106
+ .jux-dmenu-item:focus-visible { outline:2px solid hsl(var(--ring,0 0% 63%)); outline-offset:-2px; }
107
+ .jux-dmenu-item[disabled] { opacity:.5; pointer-events:none; }
108
+ .jux-dmenu-item-label { flex:1; }
109
+ .jux-dmenu-shortcut { font-size:12px; color:hsl(var(--muted-foreground,0 0% 45%)); letter-spacing:.04em; }
110
+ .jux-dmenu-arrow { font-size:10px; color:hsl(var(--muted-foreground,0 0% 45%)); }
111
+ `;
112
+ document.head.appendChild(styleEl);
113
+ }
114
+
115
+ const wrapper = document.createElement('div');
116
+ wrapper.className = 'jux-dmenu-wrapper';
117
+ wrapper.id = this._id;
118
+ if (className) wrapper.className += ` ${className}`;
119
+ if (style) wrapper.setAttribute('style', style);
120
+
121
+ const triggerBtn = document.createElement('button');
122
+ triggerBtn.className = 'jux-dmenu-trigger';
123
+ triggerBtn.textContent = trigger;
124
+ triggerBtn.type = 'button';
125
+ wrapper.appendChild(triggerBtn);
126
+
127
+ const menu = document.createElement('div');
128
+ menu.className = 'jux-dmenu-menu';
129
+ this._menuEl = menu;
130
+
131
+ this._buildItems(menu, items);
132
+
133
+ wrapper.appendChild(menu);
134
+
135
+ // Toggle
136
+ triggerBtn.addEventListener('click', (e) => { e.stopPropagation(); this.toggle(); });
137
+ document.addEventListener('click', (e) => { if (!wrapper.contains(e.target as Node)) this.close(); });
138
+
139
+ // Keyboard
140
+ triggerBtn.addEventListener('keydown', (e) => {
141
+ if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
142
+ e.preventDefault(); this.open();
143
+ const first = menu.querySelector('.jux-dmenu-item:not([disabled])') as HTMLElement;
144
+ if (first) first.focus();
145
+ }
146
+ });
147
+
148
+ menu.addEventListener('keydown', (e) => {
149
+ const btns = Array.from(menu.querySelectorAll('.jux-dmenu-item:not([disabled])')) as HTMLElement[];
150
+ const idx = btns.indexOf(document.activeElement as HTMLElement);
151
+ if (e.key === 'ArrowDown') { e.preventDefault(); btns[(idx + 1) % btns.length]?.focus(); }
152
+ else if (e.key === 'ArrowUp') { e.preventDefault(); btns[(idx - 1 + btns.length) % btns.length]?.focus(); }
153
+ else if (e.key === 'Escape') { this.close(); triggerBtn.focus(); }
154
+ });
155
+
156
+ this._wireStandardEvents(wrapper);
157
+ container.appendChild(wrapper);
158
+ this._wrapper = wrapper;
159
+
160
+ return this;
161
+ }
162
+
163
+ private _buildItems(menu: HTMLElement, items: DropdownMenuItem[]): void {
164
+ menu.innerHTML = '';
165
+
166
+ items.forEach(item => {
167
+ if (item.type === 'separator') {
168
+ const sep = document.createElement('div');
169
+ sep.className = 'jux-dmenu-sep';
170
+ menu.appendChild(sep);
171
+ } else if (item.type === 'label') {
172
+ const lbl = document.createElement('div');
173
+ lbl.className = 'jux-dmenu-label';
174
+ lbl.textContent = item.label ?? '';
175
+ menu.appendChild(lbl);
176
+ } else {
177
+ const btn = document.createElement('button');
178
+ btn.className = 'jux-dmenu-item';
179
+ btn.type = 'button';
180
+ if (item.disabled) btn.disabled = true;
181
+
182
+ const labelSpan = document.createElement('span');
183
+ labelSpan.className = 'jux-dmenu-item-label';
184
+ labelSpan.textContent = item.label ?? '';
185
+ btn.appendChild(labelSpan);
186
+
187
+ if (item.submenu) {
188
+ const arrow = document.createElement('span');
189
+ arrow.className = 'jux-dmenu-arrow';
190
+ arrow.textContent = '›';
191
+ btn.appendChild(arrow);
192
+ } else if (item.shortcut) {
193
+ const sc = document.createElement('span');
194
+ sc.className = 'jux-dmenu-shortcut';
195
+ sc.textContent = item.shortcut;
196
+ btn.appendChild(sc);
197
+ }
198
+
199
+ btn.addEventListener('click', (e) => {
200
+ if (!item.disabled) {
201
+ this._triggerCallback('select', item.value || item.label, e, this);
202
+ this.close();
203
+ }
204
+ });
205
+
206
+ menu.appendChild(btn);
207
+ }
208
+ });
209
+ }
210
+ }
211
+
212
+ export function dropdownMenu(id: string, options: DropdownMenuOptions = {}): DropdownMenu {
213
+ return new DropdownMenu(id, options);
214
+ }
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.162",
3
+ "version": "1.1.163",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",