@design.estate/dees-catalog 3.96.0 → 3.96.2
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 +2971 -2947
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.d.ts +1 -0
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +21 -11
- 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 +52 -80
- package/dist_ts_web/elements/00theme.d.ts +2 -0
- package/dist_ts_web/elements/00theme.js +7 -1
- 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-table/dees-table.ts +43 -27
- 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 +55 -91
- package/ts_web/elements/00theme.ts +8 -0
- package/dist_watch/bundle.js +0 -235637
- package/dist_watch/bundle.js.map +0 -7
- package/dist_watch/index.html +0 -28
|
@@ -735,22 +735,24 @@ export class DeesTable<T> extends DeesElement {
|
|
|
735
735
|
return html`
|
|
736
736
|
<td class="actionsCol">
|
|
737
737
|
<div class="actionsContainer">
|
|
738
|
-
${inRowActions
|
|
739
|
-
(actionArg) =>
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
738
|
+
${inRowActions
|
|
739
|
+
.filter((actionArg) => this.isActionRelevant(actionArg, itemArg))
|
|
740
|
+
.map(
|
|
741
|
+
(actionArg) => html`
|
|
742
|
+
<div
|
|
743
|
+
class="action"
|
|
744
|
+
@click=${() =>
|
|
745
|
+
actionArg.actionFunc({
|
|
746
|
+
item: itemArg,
|
|
747
|
+
table: this,
|
|
748
|
+
})}
|
|
749
|
+
>
|
|
750
|
+
${actionArg.iconName
|
|
751
|
+
? html` <dees-icon .icon=${actionArg.iconName}></dees-icon> `
|
|
752
|
+
: actionArg.name}
|
|
753
|
+
</div>
|
|
754
|
+
`
|
|
755
|
+
)}
|
|
754
756
|
</div>
|
|
755
757
|
</td>
|
|
756
758
|
`;
|
|
@@ -2119,7 +2121,11 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2119
2121
|
this.startEditing(cell.item, cell.col);
|
|
2120
2122
|
return;
|
|
2121
2123
|
}
|
|
2122
|
-
const dblAction = this.dataActions.find(
|
|
2124
|
+
const dblAction = this.dataActions.find(
|
|
2125
|
+
(action) =>
|
|
2126
|
+
action.type?.includes('doubleClick')
|
|
2127
|
+
&& this.isActionRelevant(action, cell.item)
|
|
2128
|
+
);
|
|
2123
2129
|
if (dblAction) dblAction.actionFunc({ item: cell.item, table: this });
|
|
2124
2130
|
};
|
|
2125
2131
|
|
|
@@ -2145,16 +2151,19 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2145
2151
|
this.emitSelectionChange();
|
|
2146
2152
|
this.requestUpdate();
|
|
2147
2153
|
}
|
|
2148
|
-
const userItems: plugins.tsclass.website.IMenuItem[] = this
|
|
2149
|
-
(
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
action
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2154
|
+
const userItems: plugins.tsclass.website.IMenuItem[] = this
|
|
2155
|
+
.getActionsForType('contextmenu')
|
|
2156
|
+
.filter((action) => this.isActionRelevant(action, item))
|
|
2157
|
+
.map(
|
|
2158
|
+
(action) => ({
|
|
2159
|
+
name: action.name,
|
|
2160
|
+
iconName: action.iconName as any,
|
|
2161
|
+
action: async () => {
|
|
2162
|
+
await action.actionFunc({ item, table: this });
|
|
2163
|
+
return null;
|
|
2164
|
+
},
|
|
2165
|
+
})
|
|
2166
|
+
);
|
|
2158
2167
|
const defaultItems: plugins.tsclass.website.IMenuItem[] = [
|
|
2159
2168
|
{
|
|
2160
2169
|
name:
|
|
@@ -2333,6 +2342,13 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2333
2342
|
);
|
|
2334
2343
|
}
|
|
2335
2344
|
|
|
2345
|
+
private isActionRelevant(actionArg: ITableAction<T>, itemArg: T): boolean {
|
|
2346
|
+
return (
|
|
2347
|
+
!actionArg.actionRelevancyCheckFunc
|
|
2348
|
+
|| actionArg.actionRelevancyCheckFunc(itemArg)
|
|
2349
|
+
);
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2336
2352
|
getActionsForType(typeArg: ITableAction['type'][0]) {
|
|
2337
2353
|
const actions: ITableAction[] = [];
|
|
2338
2354
|
for (const action of this.dataActions) {
|
|
@@ -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 = [
|
|
@@ -213,11 +203,14 @@ export class DeesIcon extends DeesElement {
|
|
|
213
203
|
pointer-events: none;
|
|
214
204
|
}
|
|
215
205
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
206
|
+
#iconContainer {
|
|
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%;
|
|
221
214
|
will-change: transform; /* Helps with animations */
|
|
222
215
|
contain: strict; /* Performance optimization */
|
|
223
216
|
}
|
|
@@ -244,11 +237,11 @@ export class DeesIcon extends DeesElement {
|
|
|
244
237
|
}
|
|
245
238
|
|
|
246
239
|
const effectiveIcon = this.getEffectiveIcon();
|
|
247
|
-
const container = this.shadowRoot?.querySelector('#iconContainer');
|
|
240
|
+
const container = this.shadowRoot?.querySelector('#iconContainer') as HTMLElement | null;
|
|
248
241
|
if (!container) return;
|
|
249
242
|
|
|
250
243
|
if (!effectiveIcon) {
|
|
251
|
-
container
|
|
244
|
+
this.clearIconContainer(container);
|
|
252
245
|
this.lastIcon = effectiveIcon;
|
|
253
246
|
this.lastIconSize = this.iconSize;
|
|
254
247
|
this.lastColor = this.color;
|
|
@@ -276,45 +269,16 @@ export class DeesIcon extends DeesElement {
|
|
|
276
269
|
const { type, name } = this.parseIconString(effectiveIcon);
|
|
277
270
|
|
|
278
271
|
if (type === 'lucide') {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
container.
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
// Convert to PascalCase (handles kebab-case Lucide ids like "file-text")
|
|
285
|
-
const pascalCaseName = lucideNameToPascalCase(name);
|
|
286
|
-
|
|
287
|
-
if ((lucideIcons as any)[pascalCaseName]) {
|
|
288
|
-
// Use the documented pattern from Lucide docs
|
|
289
|
-
const svgElement = createElement((lucideIcons as any)[pascalCaseName], {
|
|
290
|
-
color: this.color,
|
|
291
|
-
size: this.iconSize,
|
|
292
|
-
strokeWidth: this.strokeWidth
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
if (svgElement) {
|
|
296
|
-
// Directly append the element
|
|
297
|
-
container.appendChild(svgElement);
|
|
298
|
-
return; // Exit early since we've added the element
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// If we reach here, something went wrong
|
|
303
|
-
throw new Error(`Could not create element for ${pascalCaseName}`);
|
|
304
|
-
} catch (error) {
|
|
305
|
-
console.error(`Error rendering Lucide icon:`, error);
|
|
306
|
-
|
|
307
|
-
// Fall back to the string-based approach
|
|
308
|
-
const iconHtml = this.renderLucideIcon(name);
|
|
309
|
-
if (iconHtml) {
|
|
310
|
-
container.innerHTML = iconHtml;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
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);
|
|
313
276
|
} else {
|
|
314
|
-
container
|
|
277
|
+
this.clearIconContainer(container);
|
|
315
278
|
console.error(`dees-icon: fa:${name} is no longer supported. Use icon="lucide:${name}" or another Lucide icon.`);
|
|
316
279
|
}
|
|
317
280
|
} catch (error) {
|
|
281
|
+
this.clearIconContainer(container);
|
|
318
282
|
console.error(`Error updating icon ${effectiveIcon}:`, error);
|
|
319
283
|
}
|
|
320
284
|
}
|
|
@@ -37,6 +37,8 @@ export interface IThemeColors {
|
|
|
37
37
|
bgTertiary: string;
|
|
38
38
|
/** Recessed shell/window base; content surfaces sit elevated above it (both themes). */
|
|
39
39
|
bgCanvas: string;
|
|
40
|
+
/** Opaque system-label base used when opacity must be composited at element level. */
|
|
41
|
+
labelBase?: string;
|
|
40
42
|
textPrimary: string;
|
|
41
43
|
textSecondary: string;
|
|
42
44
|
textMuted: string;
|
|
@@ -226,6 +228,7 @@ export const themeDefaults: ITheme = {
|
|
|
226
228
|
bgSecondary: '#f2f2f7', // secondarySystemBackground / grouped
|
|
227
229
|
bgTertiary: '#e9e9eb', // tertiary recess (headers, hovers)
|
|
228
230
|
bgCanvas: '#f2f2f7', // recessed shell base (systemGroupedBackground)
|
|
231
|
+
labelBase: '#3c3c43', // opaque system label base
|
|
229
232
|
textPrimary: '#1d1d1f', // near-black label
|
|
230
233
|
textSecondary: 'rgba(60, 60, 67, 0.60)', // secondaryLabel
|
|
231
234
|
textMuted: 'rgba(60, 60, 67, 0.30)', // tertiaryLabel
|
|
@@ -258,6 +261,7 @@ export const themeDefaults: ITheme = {
|
|
|
258
261
|
bgSecondary: '#1c1c1e', // secondarySystemBackground / systemGray6 — cards, panels
|
|
259
262
|
bgTertiary: '#2c2c2e', // tertiarySystemBackground / systemGray5 — headers, hovers
|
|
260
263
|
bgCanvas: '#000000', // recessed shell base (true black, matches systemBackground)
|
|
264
|
+
labelBase: '#ebebf5', // opaque system label base
|
|
261
265
|
textPrimary: '#f5f5f7', // near-white label
|
|
262
266
|
textSecondary: 'rgba(235, 235, 245, 0.60)', // secondaryLabel
|
|
263
267
|
textMuted: 'rgba(235, 235, 245, 0.30)', // tertiaryLabel
|
|
@@ -524,6 +528,10 @@ export const themeDefaultStyles: CSSResult = css`
|
|
|
524
528
|
/* ========================================
|
|
525
529
|
* Colors — Text / Label tiers
|
|
526
530
|
* ======================================== */
|
|
531
|
+
--dees-color-label-base: ${cssManager.bdTheme(l.labelBase || '#3c3c43', d.labelBase || '#ebebf5')};
|
|
532
|
+
--dees-label-opacity-secondary: 0.6;
|
|
533
|
+
--dees-label-opacity-muted: 0.3;
|
|
534
|
+
--dees-label-opacity-quaternary: 0.18;
|
|
527
535
|
--dees-color-text-primary: ${cssManager.bdTheme(l.textPrimary, d.textPrimary)};
|
|
528
536
|
--dees-color-text-secondary: ${cssManager.bdTheme(l.textSecondary, d.textSecondary)};
|
|
529
537
|
--dees-color-text-muted: ${cssManager.bdTheme(l.textMuted, d.textMuted)};
|