juxscript 1.1.112 → 1.1.115
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/css-classes-inventory.json +342 -2
- package/dom-structure-map.json +246 -2
- package/index.d.ts +13 -0
- package/index.d.ts.map +1 -1
- package/index.js +14 -1
- package/lib/components/base/LayoutExtensions.d.ts +6 -0
- package/lib/components/base/LayoutExtensions.d.ts.map +1 -1
- package/lib/components/base/LayoutExtensions.js +19 -0
- package/lib/components/base/LayoutExtensions.ts +29 -0
- package/lib/components/image.d.ts +42 -0
- package/lib/components/image.d.ts.map +1 -0
- package/lib/components/image.js +204 -0
- package/lib/components/image.ts +260 -0
- package/lib/components/layer.d.ts +72 -0
- package/lib/components/layer.d.ts.map +1 -0
- package/lib/components/layer.js +219 -0
- package/lib/components/layer.ts +304 -0
- package/lib/components/link.d.ts +41 -0
- package/lib/components/link.d.ts.map +1 -0
- package/lib/components/link.js +216 -0
- package/lib/components/link.ts +268 -0
- package/lib/styles/gradients.css +326 -0
- package/lib/styles/link.css +158 -0
- package/package.json +1 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
+
import { renderIcon } from './icons.js';
|
|
3
|
+
import { formatIdAsLabel } from '../utils/formatId.js';
|
|
4
|
+
// Event definitions
|
|
5
|
+
const TRIGGER_EVENTS = [];
|
|
6
|
+
const CALLBACK_EVENTS = ['click'];
|
|
7
|
+
export class Link extends BaseComponent {
|
|
8
|
+
constructor(id, options = {}) {
|
|
9
|
+
super(id, {
|
|
10
|
+
visible: true,
|
|
11
|
+
disabled: options.disabled ?? false,
|
|
12
|
+
loading: false,
|
|
13
|
+
class: options.class ?? '',
|
|
14
|
+
style: options.style ?? '',
|
|
15
|
+
attributes: {},
|
|
16
|
+
href: options.href ?? `/${id}`,
|
|
17
|
+
text: options.text ?? formatIdAsLabel(id),
|
|
18
|
+
icon: options.icon ?? '',
|
|
19
|
+
iconPosition: options.iconPosition ?? 'left',
|
|
20
|
+
target: options.target ?? '_self',
|
|
21
|
+
external: options.external ?? false,
|
|
22
|
+
active: options.active ?? false
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
getTriggerEvents() {
|
|
26
|
+
return TRIGGER_EVENTS;
|
|
27
|
+
}
|
|
28
|
+
getCallbackEvents() {
|
|
29
|
+
return CALLBACK_EVENTS;
|
|
30
|
+
}
|
|
31
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
32
|
+
* FLUENT API
|
|
33
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
34
|
+
href(value) {
|
|
35
|
+
this.state.href = value;
|
|
36
|
+
if (this.container) {
|
|
37
|
+
const link = this.container.querySelector(`#${this._id}`);
|
|
38
|
+
if (link)
|
|
39
|
+
link.href = value;
|
|
40
|
+
}
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
text(value) {
|
|
44
|
+
this.state.text = value;
|
|
45
|
+
if (this.container) {
|
|
46
|
+
const link = this.container.querySelector(`#${this._id}`);
|
|
47
|
+
if (link) {
|
|
48
|
+
const textNode = link.querySelector('.jux-link-text');
|
|
49
|
+
if (textNode)
|
|
50
|
+
textNode.textContent = value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
icon(value) {
|
|
56
|
+
this.state.icon = value;
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
iconPosition(value) {
|
|
60
|
+
this.state.iconPosition = value;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
target(value) {
|
|
64
|
+
this.state.target = value;
|
|
65
|
+
if (this.container) {
|
|
66
|
+
const link = this.container.querySelector(`#${this._id}`);
|
|
67
|
+
if (link)
|
|
68
|
+
link.target = value;
|
|
69
|
+
}
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
external(value = true) {
|
|
73
|
+
this.state.external = value;
|
|
74
|
+
if (value) {
|
|
75
|
+
this.state.target = '_blank';
|
|
76
|
+
}
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
active(value = true) {
|
|
80
|
+
this.state.active = value;
|
|
81
|
+
if (this.container) {
|
|
82
|
+
const link = this.container.querySelector(`#${this._id}`);
|
|
83
|
+
if (link)
|
|
84
|
+
link.classList.toggle('jux-link-active', value);
|
|
85
|
+
}
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
89
|
+
* RENDER
|
|
90
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
91
|
+
render(targetId) {
|
|
92
|
+
const container = this._setupContainer(targetId);
|
|
93
|
+
const { href, text, icon, iconPosition, target, external, active, disabled, style, class: className } = this.state;
|
|
94
|
+
const link = document.createElement('a');
|
|
95
|
+
link.id = this._id;
|
|
96
|
+
link.className = 'jux-link';
|
|
97
|
+
if (active)
|
|
98
|
+
link.classList.add('jux-link-active');
|
|
99
|
+
if (disabled)
|
|
100
|
+
link.classList.add('jux-link-disabled');
|
|
101
|
+
if (className)
|
|
102
|
+
link.className += ` ${className}`;
|
|
103
|
+
if (style)
|
|
104
|
+
link.setAttribute('style', style);
|
|
105
|
+
link.href = href;
|
|
106
|
+
link.target = target;
|
|
107
|
+
// Add rel for external links
|
|
108
|
+
if (external || target === '_blank') {
|
|
109
|
+
link.rel = 'noopener noreferrer';
|
|
110
|
+
}
|
|
111
|
+
// Mark as external link for router to ignore
|
|
112
|
+
if (external) {
|
|
113
|
+
link.dataset.router = 'false';
|
|
114
|
+
}
|
|
115
|
+
// Icon (left position)
|
|
116
|
+
if (icon && iconPosition === 'left') {
|
|
117
|
+
const iconEl = document.createElement('span');
|
|
118
|
+
iconEl.className = 'jux-link-icon jux-link-icon-left';
|
|
119
|
+
iconEl.appendChild(renderIcon(icon));
|
|
120
|
+
link.appendChild(iconEl);
|
|
121
|
+
}
|
|
122
|
+
// Text
|
|
123
|
+
const textEl = document.createElement('span');
|
|
124
|
+
textEl.className = 'jux-link-text';
|
|
125
|
+
textEl.textContent = text;
|
|
126
|
+
link.appendChild(textEl);
|
|
127
|
+
// Icon (right position)
|
|
128
|
+
if (icon && iconPosition === 'right') {
|
|
129
|
+
const iconEl = document.createElement('span');
|
|
130
|
+
iconEl.className = 'jux-link-icon jux-link-icon-right';
|
|
131
|
+
iconEl.appendChild(renderIcon(icon));
|
|
132
|
+
link.appendChild(iconEl);
|
|
133
|
+
}
|
|
134
|
+
// Wire events
|
|
135
|
+
this._wireStandardEvents(link);
|
|
136
|
+
// Fire click callback
|
|
137
|
+
link.addEventListener('click', (e) => {
|
|
138
|
+
if (disabled) {
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this._triggerCallback('click', href);
|
|
143
|
+
});
|
|
144
|
+
// Sync href changes
|
|
145
|
+
const hrefSync = this._syncBindings.find(b => b.property === 'href');
|
|
146
|
+
if (hrefSync) {
|
|
147
|
+
const transform = hrefSync.toComponent || ((v) => String(v));
|
|
148
|
+
hrefSync.stateObj.subscribe((val) => {
|
|
149
|
+
this.href(transform(val));
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// Sync text changes
|
|
153
|
+
const textSync = this._syncBindings.find(b => b.property === 'text');
|
|
154
|
+
if (textSync) {
|
|
155
|
+
const transform = textSync.toComponent || ((v) => String(v));
|
|
156
|
+
textSync.stateObj.subscribe((val) => {
|
|
157
|
+
this.text(transform(val));
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
container.appendChild(link);
|
|
161
|
+
requestAnimationFrame(() => {
|
|
162
|
+
if (window.lucide) {
|
|
163
|
+
window.lucide.createIcons();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
update(prop, value) {
|
|
169
|
+
if (!this.container)
|
|
170
|
+
return;
|
|
171
|
+
const link = this.container.querySelector(`#${this._id}`);
|
|
172
|
+
if (!link)
|
|
173
|
+
return;
|
|
174
|
+
switch (prop) {
|
|
175
|
+
case 'href':
|
|
176
|
+
link.href = value;
|
|
177
|
+
break;
|
|
178
|
+
case 'text':
|
|
179
|
+
const textNode = link.querySelector('.jux-link-text');
|
|
180
|
+
if (textNode)
|
|
181
|
+
textNode.textContent = value;
|
|
182
|
+
break;
|
|
183
|
+
case 'active':
|
|
184
|
+
link.classList.toggle('jux-link-active', value);
|
|
185
|
+
break;
|
|
186
|
+
case 'disabled':
|
|
187
|
+
link.classList.toggle('jux-link-disabled', value);
|
|
188
|
+
break;
|
|
189
|
+
case 'target':
|
|
190
|
+
link.target = value;
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
super.update(prop, value);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export function link(id, options = {}) {
|
|
199
|
+
return new Link(id, options);
|
|
200
|
+
}
|
|
201
|
+
// Convenience helpers
|
|
202
|
+
export function navLink(id, text) {
|
|
203
|
+
return new Link(id, {
|
|
204
|
+
href: `/${id}`,
|
|
205
|
+
text: text || formatIdAsLabel(id)
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
export function externalLink(id, href, text) {
|
|
209
|
+
return new Link(id, {
|
|
210
|
+
href,
|
|
211
|
+
text: text || formatIdAsLabel(id),
|
|
212
|
+
external: true,
|
|
213
|
+
icon: 'external-link',
|
|
214
|
+
iconPosition: 'right'
|
|
215
|
+
});
|
|
216
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
|
+
import { renderIcon } from './icons.js';
|
|
3
|
+
import { formatIdAsLabel } from '../utils/formatId.js';
|
|
4
|
+
|
|
5
|
+
// Event definitions
|
|
6
|
+
const TRIGGER_EVENTS = [] as const;
|
|
7
|
+
const CALLBACK_EVENTS = ['click'] as const;
|
|
8
|
+
|
|
9
|
+
export interface LinkOptions {
|
|
10
|
+
href?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
iconPosition?: 'left' | 'right';
|
|
14
|
+
target?: '_self' | '_blank' | '_parent' | '_top';
|
|
15
|
+
external?: boolean;
|
|
16
|
+
active?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
style?: string;
|
|
19
|
+
class?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface LinkState extends BaseState {
|
|
23
|
+
href: string;
|
|
24
|
+
text: string;
|
|
25
|
+
icon: string;
|
|
26
|
+
iconPosition: 'left' | 'right';
|
|
27
|
+
target: string;
|
|
28
|
+
external: boolean;
|
|
29
|
+
active: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class Link extends BaseComponent<LinkState> {
|
|
33
|
+
constructor(id: string, options: LinkOptions = {}) {
|
|
34
|
+
super(id, {
|
|
35
|
+
visible: true,
|
|
36
|
+
disabled: options.disabled ?? false,
|
|
37
|
+
loading: false,
|
|
38
|
+
class: options.class ?? '',
|
|
39
|
+
style: options.style ?? '',
|
|
40
|
+
attributes: {},
|
|
41
|
+
href: options.href ?? `/${id}`,
|
|
42
|
+
text: options.text ?? formatIdAsLabel(id),
|
|
43
|
+
icon: options.icon ?? '',
|
|
44
|
+
iconPosition: options.iconPosition ?? 'left',
|
|
45
|
+
target: options.target ?? '_self',
|
|
46
|
+
external: options.external ?? false,
|
|
47
|
+
active: options.active ?? false
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected getTriggerEvents(): readonly string[] {
|
|
52
|
+
return TRIGGER_EVENTS;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected getCallbackEvents(): readonly string[] {
|
|
56
|
+
return CALLBACK_EVENTS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
60
|
+
* FLUENT API
|
|
61
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
62
|
+
|
|
63
|
+
href(value: string): this {
|
|
64
|
+
this.state.href = value;
|
|
65
|
+
if (this.container) {
|
|
66
|
+
const link = this.container.querySelector(`#${this._id}`) as HTMLAnchorElement;
|
|
67
|
+
if (link) link.href = value;
|
|
68
|
+
}
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
text(value: string): this {
|
|
73
|
+
this.state.text = value;
|
|
74
|
+
if (this.container) {
|
|
75
|
+
const link = this.container.querySelector(`#${this._id}`) as HTMLAnchorElement;
|
|
76
|
+
if (link) {
|
|
77
|
+
const textNode = link.querySelector('.jux-link-text');
|
|
78
|
+
if (textNode) textNode.textContent = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
icon(value: string): this {
|
|
85
|
+
this.state.icon = value;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
iconPosition(value: 'left' | 'right'): this {
|
|
90
|
+
this.state.iconPosition = value;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
target(value: '_self' | '_blank' | '_parent' | '_top'): this {
|
|
95
|
+
this.state.target = value;
|
|
96
|
+
if (this.container) {
|
|
97
|
+
const link = this.container.querySelector(`#${this._id}`) as HTMLAnchorElement;
|
|
98
|
+
if (link) link.target = value;
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
external(value: boolean = true): this {
|
|
104
|
+
this.state.external = value;
|
|
105
|
+
if (value) {
|
|
106
|
+
this.state.target = '_blank';
|
|
107
|
+
}
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
active(value: boolean = true): this {
|
|
112
|
+
this.state.active = value;
|
|
113
|
+
if (this.container) {
|
|
114
|
+
const link = this.container.querySelector(`#${this._id}`) as HTMLAnchorElement;
|
|
115
|
+
if (link) link.classList.toggle('jux-link-active', value);
|
|
116
|
+
}
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
121
|
+
* RENDER
|
|
122
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
123
|
+
|
|
124
|
+
render(targetId?: string | HTMLElement | BaseComponent<any>): this {
|
|
125
|
+
const container = this._setupContainer(targetId);
|
|
126
|
+
|
|
127
|
+
const { href, text, icon, iconPosition, target, external, active, disabled, style, class: className } = this.state;
|
|
128
|
+
|
|
129
|
+
const link = document.createElement('a');
|
|
130
|
+
link.id = this._id;
|
|
131
|
+
link.className = 'jux-link';
|
|
132
|
+
if (active) link.classList.add('jux-link-active');
|
|
133
|
+
if (disabled) link.classList.add('jux-link-disabled');
|
|
134
|
+
if (className) link.className += ` ${className}`;
|
|
135
|
+
if (style) link.setAttribute('style', style);
|
|
136
|
+
|
|
137
|
+
link.href = href;
|
|
138
|
+
link.target = target;
|
|
139
|
+
|
|
140
|
+
// Add rel for external links
|
|
141
|
+
if (external || target === '_blank') {
|
|
142
|
+
link.rel = 'noopener noreferrer';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Mark as external link for router to ignore
|
|
146
|
+
if (external) {
|
|
147
|
+
link.dataset.router = 'false';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Icon (left position)
|
|
151
|
+
if (icon && iconPosition === 'left') {
|
|
152
|
+
const iconEl = document.createElement('span');
|
|
153
|
+
iconEl.className = 'jux-link-icon jux-link-icon-left';
|
|
154
|
+
iconEl.appendChild(renderIcon(icon));
|
|
155
|
+
link.appendChild(iconEl);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Text
|
|
159
|
+
const textEl = document.createElement('span');
|
|
160
|
+
textEl.className = 'jux-link-text';
|
|
161
|
+
textEl.textContent = text;
|
|
162
|
+
link.appendChild(textEl);
|
|
163
|
+
|
|
164
|
+
// Icon (right position)
|
|
165
|
+
if (icon && iconPosition === 'right') {
|
|
166
|
+
const iconEl = document.createElement('span');
|
|
167
|
+
iconEl.className = 'jux-link-icon jux-link-icon-right';
|
|
168
|
+
iconEl.appendChild(renderIcon(icon));
|
|
169
|
+
link.appendChild(iconEl);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Wire events
|
|
173
|
+
this._wireStandardEvents(link);
|
|
174
|
+
|
|
175
|
+
// Fire click callback
|
|
176
|
+
link.addEventListener('click', (e) => {
|
|
177
|
+
if (disabled) {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this._triggerCallback('click', href);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Sync href changes
|
|
185
|
+
const hrefSync = this._syncBindings.find(b => b.property === 'href');
|
|
186
|
+
if (hrefSync) {
|
|
187
|
+
const transform = hrefSync.toComponent || ((v: any) => String(v));
|
|
188
|
+
hrefSync.stateObj.subscribe((val: any) => {
|
|
189
|
+
this.href(transform(val));
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Sync text changes
|
|
194
|
+
const textSync = this._syncBindings.find(b => b.property === 'text');
|
|
195
|
+
if (textSync) {
|
|
196
|
+
const transform = textSync.toComponent || ((v: any) => String(v));
|
|
197
|
+
textSync.stateObj.subscribe((val: any) => {
|
|
198
|
+
this.text(transform(val));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
container.appendChild(link);
|
|
203
|
+
|
|
204
|
+
requestAnimationFrame(() => {
|
|
205
|
+
if ((window as any).lucide) {
|
|
206
|
+
(window as any).lucide.createIcons();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
update(prop: string, value: any): void {
|
|
214
|
+
if (!this.container) return;
|
|
215
|
+
|
|
216
|
+
const link = this.container.querySelector(`#${this._id}`) as HTMLAnchorElement;
|
|
217
|
+
if (!link) return;
|
|
218
|
+
|
|
219
|
+
switch (prop) {
|
|
220
|
+
case 'href':
|
|
221
|
+
link.href = value;
|
|
222
|
+
break;
|
|
223
|
+
|
|
224
|
+
case 'text':
|
|
225
|
+
const textNode = link.querySelector('.jux-link-text');
|
|
226
|
+
if (textNode) textNode.textContent = value;
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
case 'active':
|
|
230
|
+
link.classList.toggle('jux-link-active', value);
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
case 'disabled':
|
|
234
|
+
link.classList.toggle('jux-link-disabled', value);
|
|
235
|
+
break;
|
|
236
|
+
|
|
237
|
+
case 'target':
|
|
238
|
+
link.target = value;
|
|
239
|
+
break;
|
|
240
|
+
|
|
241
|
+
default:
|
|
242
|
+
super.update(prop, value);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function link(id: string, options: LinkOptions = {}): Link {
|
|
249
|
+
return new Link(id, options);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Convenience helpers
|
|
253
|
+
export function navLink(id: string, text?: string): Link {
|
|
254
|
+
return new Link(id, {
|
|
255
|
+
href: `/${id}`,
|
|
256
|
+
text: text || formatIdAsLabel(id)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function externalLink(id: string, href: string, text?: string): Link {
|
|
261
|
+
return new Link(id, {
|
|
262
|
+
href,
|
|
263
|
+
text: text || formatIdAsLabel(id),
|
|
264
|
+
external: true,
|
|
265
|
+
icon: 'external-link',
|
|
266
|
+
iconPosition: 'right'
|
|
267
|
+
});
|
|
268
|
+
}
|