Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/pages/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .github/pages/src/components/TokenField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<TokenTag
{token}
{tokenProperties}
{category}
onremove={() => removeToken(category, index)}
onupdate={(key, value) => updateToken(category, index, key, value)}
/>
Expand Down
9 changes: 8 additions & 1 deletion .github/pages/src/components/TokenPopover.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script>
import { untrack } from 'svelte';
import { SWIFTUI_BUILTIN_STYLES } from '../lib/validation.js';
import { camelToWords } from '../lib/utils.js';

let { token, tokenProperties = [], onupdate, onclose } = $props();
let { token, tokenProperties = [], duplicateCategory = null, onupdate, onclose } = $props();

// Local input values — initialized from token, never overwritten by re-renders
let values = $state(
Expand All @@ -25,6 +26,8 @@
SWIFTUI_BUILTIN_STYLES.has(values.style || token.name) && !values.style
);

let showDuplicateWarning = $derived(duplicateCategory !== null);

function handleKeydown(e) {
if (e.key === 'Escape') onclose();
}
Expand All @@ -40,6 +43,10 @@
<div class="popover-warning">
Shadows SwiftUI's built-in <code>.{token.name}</code> — set a custom style name below.
</div>
{:else if showDuplicateWarning}
<div class="popover-warning">
Style name <code>.{values.style || token.name}</code> is already used by another token in {camelToWords(duplicateCategory)}. This will cause a compiler error.
</div>
{/if}
{#each tokenProperties as prop}
<div class="popover-field">
Expand Down
16 changes: 13 additions & 3 deletions .github/pages/src/components/TokenTag.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script>
import TokenPopover from './TokenPopover.svelte';
import { hasBuiltinConflict } from '../lib/validation.js';
import { hasBuiltinConflict, findDuplicateStyleName } from '../lib/validation.js';
import { getAllStyles, getEnabledCategories } from '../lib/state.svelte.js';

let { token, tokenProperties = [], onremove, onupdate } = $props();
let { token, tokenProperties = [], category, onremove, onupdate } = $props();

let showPopover = $state(false);

Expand All @@ -16,16 +17,24 @@

let hasOverride = $derived(token.name !== token.style);
let hasConflict = $derived(hasBuiltinConflict(token));
let duplicateCategory = $derived(findDuplicateStyleName(category, token, getAllStyles(), getEnabledCategories()));
let hasDuplicate = $derived(duplicateCategory !== null);
</script>

<span class="token-tag" class:has-override={hasOverride} class:has-conflict={hasConflict}>
<span class="token-tag" class:has-override={hasOverride} class:has-conflict={hasConflict || hasDuplicate}>
<span class="tag-name">{token.name}</span>
{#if hasConflict}
<span class="tag-warning" title="Shadows SwiftUI's built-in .{token.name} — open to set a custom style name">
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
<path d="M8.982 1.566a1.13 1.13 0 00-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 01-1.1 0L7.1 5.995A.905.905 0 018 5zm.002 6a1 1 0 110 2 1 1 0 010-2z"/>
</svg>
</span>
{:else if hasDuplicate}
<span class="tag-warning" title="Style name &quot;{token.style ?? token.name}&quot; conflicts with a token in {duplicateCategory} — open to set a different style name">
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
<path d="M8.982 1.566a1.13 1.13 0 00-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 01-1.1 0L7.1 5.995A.905.905 0 018 5zm.002 6a1 1 0 110 2 1 1 0 010-2z"/>
</svg>
</span>
{/if}
<button class="tag-btn info-btn" onclick={togglePopover} aria-label="Edit token properties">
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
Expand All @@ -41,6 +50,7 @@
<TokenPopover
{token}
{tokenProperties}
{duplicateCategory}
onupdate={handleUpdate}
onclose={() => showPopover = false}
/>
Expand Down
7 changes: 7 additions & 0 deletions .github/pages/src/lib/state.svelte.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ export function getTokens(category) {
return styles[category] ?? [];
}

/**
* Get all styles (for validation purposes).
*/
export function getAllStyles() {
return styles;
}

/**
* Get a config property value.
*/
Expand Down
33 changes: 33 additions & 0 deletions .github/pages/src/lib/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,36 @@ export const SWIFTUI_BUILTIN_STYLES = new Set([
export function hasBuiltinConflict(token) {
return token.name === token.style && SWIFTUI_BUILTIN_STYLES.has(token.style);
}

/**
* Check if a token has a duplicate style name across all enabled categories.
* Returns the conflicting category name if found, null otherwise.
* @param {string} currentCategory - The category of the current token
* @param {object} token - The token to check (must have name and style properties)
* @param {object} allStyles - Object with category keys and token array values
* @param {Set} enabledCategories - Set of enabled category names
* @returns {string|null} - The conflicting category name or null
*/
export function findDuplicateStyleName(currentCategory, token, allStyles, enabledCategories) {
const effectiveStyle = token.style ?? token.name;

for (const cat of enabledCategories) {
const tokens = allStyles[cat] ?? [];

for (const otherToken of tokens) {
const otherEffectiveStyle = otherToken.style ?? otherToken.name;

// Skip if it's the same token (same category and same name)
if (cat === currentCategory && otherToken.name === token.name) {
continue;
}

// Found a duplicate
if (effectiveStyle === otherEffectiveStyle) {
return cat;
}
}
}

return null;
}