@design.estate/dees-catalog 3.96.1 → 3.96.3
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_bundle/bundle.js +2951 -2959
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-dataview/dees-statsgrid/dees-statsgrid.js +5 -6
- package/dist_ts_web/elements/00group-dataview/dees-table/styles.js +1 -8
- package/dist_ts_web/elements/00group-input/dees-input-multitoggle/dees-input-multitoggle.js +20 -4
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +2 -1
- package/dist_ts_web/elements/00group-utility/dees-icon/dees-icon.d.ts +2 -1
- package/dist_ts_web/elements/00group-utility/dees-icon/dees-icon.js +51 -90
- package/package.json +3 -3
- package/readme.md +5 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-dataview/dees-statsgrid/dees-statsgrid.ts +5 -6
- package/ts_web/elements/00group-dataview/dees-table/styles.ts +0 -7
- package/ts_web/elements/00group-input/dees-input-multitoggle/dees-input-multitoggle.ts +19 -3
- package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +1 -0
- package/ts_web/elements/00group-utility/dees-icon/dees-icon.ts +54 -101
|
@@ -823,14 +823,13 @@ export class DeesStatsGrid extends DeesElement {
|
|
|
823
823
|
<div class="grid-title"></div>
|
|
824
824
|
<div class="grid-actions">
|
|
825
825
|
${this.gridActions.map(action => html`
|
|
826
|
-
<dees-button
|
|
826
|
+
<dees-button
|
|
827
827
|
@clicked=${() => this.handleGridAction(action)}
|
|
828
828
|
type="outline"
|
|
829
829
|
size="sm"
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
</dees-button>
|
|
830
|
+
.text=${action.name}
|
|
831
|
+
.icon=${action.iconName || ''}
|
|
832
|
+
></dees-button>
|
|
834
833
|
`)}
|
|
835
834
|
</div>
|
|
836
835
|
</div>
|
|
@@ -1249,4 +1248,4 @@ export class DeesStatsGrid extends DeesElement {
|
|
|
1249
1248
|
// open an empty menu.)
|
|
1250
1249
|
DeesContextmenu.openContextMenuWithOptions(event, tile.actions as any);
|
|
1251
1250
|
}
|
|
1252
|
-
}
|
|
1251
|
+
}
|
|
@@ -515,7 +515,6 @@ export const tableStyles: CSSResult[] = [
|
|
|
515
515
|
height: var(--dees-control-height-sm);
|
|
516
516
|
border-radius: var(--dees-radius-sm);
|
|
517
517
|
color: var(--dees-color-text-muted);
|
|
518
|
-
--dees-icon-opacity: var(--dees-label-opacity-muted);
|
|
519
518
|
cursor: pointer;
|
|
520
519
|
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
521
520
|
}
|
|
@@ -523,7 +522,6 @@ export const tableStyles: CSSResult[] = [
|
|
|
523
522
|
.action:hover {
|
|
524
523
|
background: var(--dees-color-badge-default-bg);
|
|
525
524
|
color: var(--dees-color-text-primary);
|
|
526
|
-
--dees-icon-opacity: 1;
|
|
527
525
|
}
|
|
528
526
|
|
|
529
527
|
.action:active {
|
|
@@ -531,15 +529,10 @@ export const tableStyles: CSSResult[] = [
|
|
|
531
529
|
}
|
|
532
530
|
|
|
533
531
|
.action dees-icon {
|
|
534
|
-
color: var(--dees-color-label-base);
|
|
535
532
|
width: 16px;
|
|
536
533
|
height: 16px;
|
|
537
534
|
}
|
|
538
535
|
|
|
539
|
-
.action:hover dees-icon {
|
|
540
|
-
color: var(--dees-color-text-primary);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
536
|
.footer {
|
|
544
537
|
display: flex;
|
|
545
538
|
align-items: center;
|
|
@@ -89,8 +89,8 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
|
|
|
89
89
|
|
|
90
90
|
.option {
|
|
91
91
|
position: relative;
|
|
92
|
-
display:
|
|
93
|
-
|
|
92
|
+
display: grid;
|
|
93
|
+
place-items: center;
|
|
94
94
|
height: 100%;
|
|
95
95
|
padding: 0 var(--dees-spacing-lg);
|
|
96
96
|
border-radius: var(--dees-radius-sm);
|
|
@@ -104,6 +104,21 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
|
|
|
104
104
|
z-index: 2;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
.option-label,
|
|
108
|
+
.option::after {
|
|
109
|
+
grid-area: 1 / 1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Reserve the selected label's semibold width in every state so moving
|
|
113
|
+
the thumb never changes the segmented control's intrinsic width. */
|
|
114
|
+
.option::after {
|
|
115
|
+
content: attr(data-option);
|
|
116
|
+
visibility: hidden;
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
user-select: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
107
122
|
/* Hairline separators between unselected segments; they fade out next to
|
|
108
123
|
the thumb. */
|
|
109
124
|
.option::before {
|
|
@@ -197,8 +212,9 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
|
|
|
197
212
|
index === 0 || isSelected || index - 1 === selectedIndex;
|
|
198
213
|
return html`<div
|
|
199
214
|
class="option ${isSelected ? 'selected' : ''} ${hideSeparator ? 'no-sep' : ''}"
|
|
215
|
+
data-option=${option}
|
|
200
216
|
@click=${() => this.handleSelection(option)}
|
|
201
|
-
>${option}</div>`;
|
|
217
|
+
><span class="option-label">${option}</span></div>`;
|
|
202
218
|
})}
|
|
203
219
|
</div>
|
|
204
220
|
</div>
|
|
@@ -37,16 +37,17 @@ export type IconWithPrefix = `lucide:${string}` | string;
|
|
|
37
37
|
// Legacy type kept for consumers that still import it. `iconFA` itself is no longer supported.
|
|
38
38
|
export type TIconKey = string;
|
|
39
39
|
|
|
40
|
-
//
|
|
41
|
-
|
|
40
|
+
// Cache opaque mask sources. Color is applied separately through currentColor,
|
|
41
|
+
// so theme and inherited text-color changes never require rebuilding geometry.
|
|
42
|
+
const iconMaskCache = new Map<string, string>();
|
|
42
43
|
|
|
43
44
|
// Clear cache items occasionally to prevent memory leaks
|
|
44
45
|
const MAX_CACHE_SIZE = 500;
|
|
45
46
|
function limitCacheSize() {
|
|
46
|
-
if (
|
|
47
|
+
if (iconMaskCache.size > MAX_CACHE_SIZE) {
|
|
47
48
|
// Remove oldest entries (first 20% of items)
|
|
48
|
-
const keysToDelete = Array.from(
|
|
49
|
-
keysToDelete.forEach(key =>
|
|
49
|
+
const keysToDelete = Array.from(iconMaskCache.keys()).slice(0, MAX_CACHE_SIZE / 5);
|
|
50
|
+
keysToDelete.forEach(key => iconMaskCache.delete(key));
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -151,53 +152,42 @@ export class DeesIcon extends DeesElement {
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
private
|
|
155
|
-
|
|
156
|
-
const cacheKey = `lucide:${
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (iconCache.has(cacheKey)) {
|
|
160
|
-
return iconCache.get(cacheKey) || '';
|
|
161
|
-
}
|
|
155
|
+
private createLucideMaskImage(iconName: string): string {
|
|
156
|
+
const pascalCaseName = lucideNameToPascalCase(iconName);
|
|
157
|
+
const cacheKey = `lucide:${pascalCaseName}:${this.iconSize}:${this.strokeWidth}`;
|
|
158
|
+
const cachedMask = iconMaskCache.get(cacheKey);
|
|
159
|
+
if (cachedMask) return cachedMask;
|
|
162
160
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// Check if the icon exists in lucideIcons
|
|
168
|
-
if (!(lucideIcons as any)[pascalCaseName]) {
|
|
169
|
-
console.warn(`Lucide icon '${pascalCaseName}' not found in lucideIcons object`);
|
|
170
|
-
return '';
|
|
171
|
-
}
|
|
161
|
+
if (!(lucideIcons as any)[pascalCaseName]) {
|
|
162
|
+
throw new Error(`Lucide icon '${pascalCaseName}' was not found`);
|
|
163
|
+
}
|
|
172
164
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!svgElement) {
|
|
181
|
-
console.warn(`createElement returned empty result for ${pascalCaseName}`);
|
|
182
|
-
return '';
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Get the HTML
|
|
186
|
-
const result = svgElement.outerHTML;
|
|
187
|
-
|
|
188
|
-
// Cache the result for future use
|
|
189
|
-
iconCache.set(cacheKey, result);
|
|
190
|
-
limitCacheSize();
|
|
191
|
-
|
|
192
|
-
return result;
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error(`Error rendering Lucide icon ${iconName}:`, error);
|
|
195
|
-
|
|
196
|
-
// Create a fallback SVG with the icon name
|
|
197
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${this.iconSize}" height="${this.iconSize}" viewBox="0 0 24 24" fill="none" stroke="${this.color}" stroke-width="${this.strokeWidth}" stroke-linecap="round" stroke-linejoin="round">
|
|
198
|
-
<text x="50%" y="50%" font-size="6" text-anchor="middle" dominant-baseline="middle" fill="${this.color}">${iconName}</text>
|
|
199
|
-
</svg>`;
|
|
165
|
+
const svgElement = createElement((lucideIcons as any)[pascalCaseName], {
|
|
166
|
+
color: '#fff',
|
|
167
|
+
size: this.iconSize,
|
|
168
|
+
strokeWidth: this.strokeWidth,
|
|
169
|
+
});
|
|
170
|
+
if (!svgElement) {
|
|
171
|
+
throw new Error(`Lucide did not create an element for '${pascalCaseName}'`);
|
|
200
172
|
}
|
|
173
|
+
|
|
174
|
+
// The mask must be completely opaque. The consumer's inherited color,
|
|
175
|
+
// including any alpha, is painted exactly once behind this geometry.
|
|
176
|
+
svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
177
|
+
svgElement.setAttribute('color', '#fff');
|
|
178
|
+
svgElement.setAttribute('stroke', '#fff');
|
|
179
|
+
svgElement.setAttribute('fill', 'none');
|
|
180
|
+
|
|
181
|
+
const maskImage = `url("data:image/svg+xml,${encodeURIComponent(svgElement.outerHTML)}")`;
|
|
182
|
+
iconMaskCache.set(cacheKey, maskImage);
|
|
183
|
+
limitCacheSize();
|
|
184
|
+
return maskImage;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private clearIconContainer(container: HTMLElement): void {
|
|
188
|
+
container.style.backgroundColor = 'transparent';
|
|
189
|
+
container.style.setProperty('mask-image', 'none');
|
|
190
|
+
container.style.setProperty('-webkit-mask-image', 'none');
|
|
201
191
|
}
|
|
202
192
|
|
|
203
193
|
public static styles = [
|
|
@@ -214,21 +204,13 @@ export class DeesIcon extends DeesElement {
|
|
|
214
204
|
}
|
|
215
205
|
|
|
216
206
|
#iconContainer {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
opacity: var(--dees-icon-opacity, 1);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/* Improve rendering performance */
|
|
228
|
-
#iconContainer svg {
|
|
229
|
-
display: block;
|
|
230
|
-
height: 100%;
|
|
231
|
-
width: 100%;
|
|
207
|
+
background-color: currentColor;
|
|
208
|
+
mask-position: center;
|
|
209
|
+
mask-repeat: no-repeat;
|
|
210
|
+
mask-size: 100% 100%;
|
|
211
|
+
-webkit-mask-position: center;
|
|
212
|
+
-webkit-mask-repeat: no-repeat;
|
|
213
|
+
-webkit-mask-size: 100% 100%;
|
|
232
214
|
will-change: transform; /* Helps with animations */
|
|
233
215
|
contain: strict; /* Performance optimization */
|
|
234
216
|
}
|
|
@@ -255,11 +237,11 @@ export class DeesIcon extends DeesElement {
|
|
|
255
237
|
}
|
|
256
238
|
|
|
257
239
|
const effectiveIcon = this.getEffectiveIcon();
|
|
258
|
-
const container = this.shadowRoot?.querySelector('#iconContainer');
|
|
240
|
+
const container = this.shadowRoot?.querySelector('#iconContainer') as HTMLElement | null;
|
|
259
241
|
if (!container) return;
|
|
260
242
|
|
|
261
243
|
if (!effectiveIcon) {
|
|
262
|
-
container
|
|
244
|
+
this.clearIconContainer(container);
|
|
263
245
|
this.lastIcon = effectiveIcon;
|
|
264
246
|
this.lastIconSize = this.iconSize;
|
|
265
247
|
this.lastColor = this.color;
|
|
@@ -287,45 +269,16 @@ export class DeesIcon extends DeesElement {
|
|
|
287
269
|
const { type, name } = this.parseIconString(effectiveIcon);
|
|
288
270
|
|
|
289
271
|
if (type === 'lucide') {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
container.
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
// Convert to PascalCase (handles kebab-case Lucide ids like "file-text")
|
|
296
|
-
const pascalCaseName = lucideNameToPascalCase(name);
|
|
297
|
-
|
|
298
|
-
if ((lucideIcons as any)[pascalCaseName]) {
|
|
299
|
-
// Use the documented pattern from Lucide docs
|
|
300
|
-
const svgElement = createElement((lucideIcons as any)[pascalCaseName], {
|
|
301
|
-
color: this.color,
|
|
302
|
-
size: this.iconSize,
|
|
303
|
-
strokeWidth: this.strokeWidth
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
if (svgElement) {
|
|
307
|
-
// Directly append the element
|
|
308
|
-
container.appendChild(svgElement);
|
|
309
|
-
return; // Exit early since we've added the element
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// If we reach here, something went wrong
|
|
314
|
-
throw new Error(`Could not create element for ${pascalCaseName}`);
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error(`Error rendering Lucide icon:`, error);
|
|
317
|
-
|
|
318
|
-
// Fall back to the string-based approach
|
|
319
|
-
const iconHtml = this.renderLucideIcon(name);
|
|
320
|
-
if (iconHtml) {
|
|
321
|
-
container.innerHTML = iconHtml;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
272
|
+
const maskImage = this.createLucideMaskImage(name);
|
|
273
|
+
container.style.backgroundColor = this.color === 'currentColor' ? '' : this.color;
|
|
274
|
+
container.style.setProperty('mask-image', maskImage);
|
|
275
|
+
container.style.setProperty('-webkit-mask-image', maskImage);
|
|
324
276
|
} else {
|
|
325
|
-
container
|
|
277
|
+
this.clearIconContainer(container);
|
|
326
278
|
console.error(`dees-icon: fa:${name} is no longer supported. Use icon="lucide:${name}" or another Lucide icon.`);
|
|
327
279
|
}
|
|
328
280
|
} catch (error) {
|
|
281
|
+
this.clearIconContainer(container);
|
|
329
282
|
console.error(`Error updating icon ${effectiveIcon}:`, error);
|
|
330
283
|
}
|
|
331
284
|
}
|