@widgetic/editor 4.0.1 → 4.0.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.
@@ -78,7 +78,7 @@
78
78
  * @prop {Function} onFocus - Optional focus handler
79
79
  */
80
80
  export let label: string;
81
- export let value: { family: string; weight: string; category: string } | null = null;
81
+ export let value: { family: string; weight: string; category: string } | string | null = null;
82
82
  export let onChange: ((newValue: { family: string; weight: string; category: string }) => void) | undefined = undefined;
83
83
  export let description: string | undefined = undefined;
84
84
  export let controllerId: string;
@@ -91,7 +91,7 @@
91
91
 
92
92
  // Font weight state - remove localStorage check on init
93
93
  let selectedWeight = `${controllerId}-Regular`;
94
- let availableWeights: string[] = [];
94
+ let availableWeights: string[] = ['Regular', 'Medium', 'SemiBold', 'Bold'];
95
95
 
96
96
  // Font search and filter state
97
97
  let allFonts: FontItem[] = [];
@@ -120,6 +120,25 @@
120
120
  export let hideRecentFromList: boolean = false;
121
121
  export let hideTrashIcon: boolean = true;
122
122
 
123
+ function coerceFontValue(raw: typeof value): { family: string; weight: string; category: string } | null {
124
+ if (!raw) return null;
125
+ if (typeof raw === 'object' && raw.family) {
126
+ return {
127
+ family: raw.family,
128
+ weight: raw.weight || 'Regular',
129
+ category: raw.category || 'Sans Serif',
130
+ };
131
+ }
132
+ if (typeof raw === 'string' && raw.trim()) {
133
+ const family = raw.split(',')[0].replace(/['"]/g, '').trim();
134
+ if (!family) return null;
135
+ return { family, weight: 'Regular', category: 'Sans Serif' };
136
+ }
137
+ return null;
138
+ }
139
+
140
+ $: resolvedFont = coerceFontValue(value);
141
+
123
142
  // Add scroll state tracking
124
143
  let isScrolled = false;
125
144
 
@@ -188,9 +207,12 @@
188
207
  onMount(async () => {
189
208
  try {
190
209
  if (browser) {
191
- // If we have an initial value, use its weight
192
- if (value?.weight) {
193
- selectedWeight = `${controllerId}-${value.weight}`;
210
+ const coerced = coerceFontValue(value);
211
+ if (coerced?.weight) {
212
+ selectedWeight = `${controllerId}-${coerced.weight}`;
213
+ }
214
+ if (typeof value === 'string' && coerced && onChange) {
215
+ onChange(coerced);
194
216
  }
195
217
 
196
218
  // Load saved filter
@@ -1368,8 +1390,8 @@
1368
1390
  }
1369
1391
  }
1370
1392
 
1371
- $: if (value?.family) {
1372
- fetchFontWeights(value.family);
1393
+ $: if (resolvedFont?.family) {
1394
+ fetchFontWeights(resolvedFont.family);
1373
1395
  }
1374
1396
 
1375
1397
  /**
@@ -1597,7 +1619,7 @@
1597
1619
  }
1598
1620
 
1599
1621
  function isFontSelected(font: FontItem) {
1600
- return value?.family === font.family;
1622
+ return resolvedFont?.family === font.family;
1601
1623
  }
1602
1624
 
1603
1625
  function isCustomFont(font: FontItem) {
@@ -1617,30 +1639,25 @@
1617
1639
  }
1618
1640
  </script>
1619
1641
 
1620
- <!-- Font Input Container -->
1621
- <div class="flex flex-col widgetic-colors">
1622
- <!-- Font Family Section -->
1623
- <div class="flex flex-col gap-[8px]">
1624
- <div class="flex items-center w-full relative h-[48px]">
1625
- <div class="w-[185px] flex-shrink-0 ml-auto">
1626
- <InputControllerHeader
1627
- label={label}
1628
- helpText={description}
1629
- />
1630
- </div>
1631
-
1632
- <div class="absolute right-0">
1633
- <div class="h-[48px] w-[185px] rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green">
1642
+ <!-- Dual IC: labels left, 185px inputs on the right (even when label is empty) -->
1643
+ <div class="font-ic flex flex-col gap-2 w-full min-w-0 widgetic-colors">
1644
+ <div class="font-family-row flex items-center justify-between w-full min-w-0 gap-2" style="min-height: 48px;">
1645
+ <div class="font-family-label-ct flex-1 min-w-0">
1646
+ {#if label}
1647
+ <InputControllerHeader {label} />
1648
+ {/if}
1649
+ </div>
1650
+ <div class="font-family-input-ct h-12 shrink-0 rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green" style="width: 185px; max-width: 185px; min-width: 185px; margin-left: auto;">
1634
1651
  <!-- Font Family Button -->
1635
1652
  <Popover.Root
1636
1653
  open={$openDropdowns?.has(controllerId)}
1637
1654
  onOpenChange={(isOpen) => {
1638
1655
  if (isOpen !== $openDropdowns?.has(controllerId)) {
1639
- if (!isOpen && value?.family && !shouldSkipFont(value.family)) {
1656
+ if (!isOpen && resolvedFont?.family && !shouldSkipFont(resolvedFont.family)) {
1640
1657
  fontUtils.addRecentFont({
1641
- family: value.family,
1642
- category: normalizeCategory(value.category) as FontItem['category'],
1643
- isCustom: fontManagerInstance?.isCustomFont(value.family) || false
1658
+ family: resolvedFont.family,
1659
+ category: normalizeCategory(resolvedFont.category) as FontItem['category'],
1660
+ isCustom: fontManagerInstance?.isCustomFont(resolvedFont.family) || false
1644
1661
  });
1645
1662
  } else if (isOpen) {
1646
1663
  // Reset active search index when opening the dropdown
@@ -1676,9 +1693,9 @@
1676
1693
  >
1677
1694
  <span
1678
1695
  class="truncate flex-1 text-left subtext text-grey7"
1679
- style="font-family: '{value?.family || 'system-ui'}', sans-serif; font-display: swap;"
1696
+ style="font-family: '{resolvedFont?.family || 'system-ui'}', sans-serif; font-display: swap;"
1680
1697
  >
1681
- {value?.family || "Select font..."}
1698
+ {resolvedFont?.family || "Select font..."}
1682
1699
  </span>
1683
1700
  <IconCaretDownFilled class="w-5 h-5 shrink-0 text-grey5 group-hover:text-grey7" />
1684
1701
  </Popover.Trigger>
@@ -1809,9 +1826,9 @@
1809
1826
  {#each filteredFonts as font, index (`${font.family}-${searchTerm ? 'search' : currentFilter}-${index}`)}
1810
1827
  <!-- svelte-ignore a11y_click_events_have_key_events -->
1811
1828
  <div
1812
- class="w-full py-1 px-4 cursor-pointer overflow-hidden relative rounded-small h-[42px] flex items-center
1813
- {value?.family === font.family ? 'bg-sage-green text-white selected-item' :
1814
- index === activeSearchIndex && searchTerm ? 'bg-light-green text-sage-green' :
1829
+ class="font-list-row w-full py-1 px-4 cursor-pointer overflow-hidden relative rounded-small h-[42px] flex items-center
1830
+ {isFontSelected(font) ? 'bg-sage-green selected-item' :
1831
+ index === activeSearchIndex && searchTerm ? 'bg-light-green' :
1815
1832
  'hover:bg-light-green'}"
1816
1833
  data-font={font.family}
1817
1834
  use:observeFont
@@ -1819,12 +1836,13 @@
1819
1836
  onmouseenter={() => handleMouseEnter(index)}
1820
1837
  role="button"
1821
1838
  tabindex="0"
1839
+ style="color: {isFontSelected(font) ? '#ffffff' : 'hsl(var(--grey7))'};"
1822
1840
  >
1823
1841
  <div class="flex items-center flex-1 min-w-0 group">
1824
- <Check class="mr-2 h-6 w-6 flex-shrink-0 {value?.family === font.family ? 'text-white' : 'text-sage-green opacity-0'}"/>
1842
+ <Check class="mr-2 h-6 w-6 flex-shrink-0 {isFontSelected(font) ? 'text-white' : 'text-sage-green opacity-0'}"/>
1825
1843
  <span
1826
1844
  class="text-[24px] truncate {index === activeSearchIndex && searchTerm ? 'font-medium' : ''}"
1827
- style="font-family: '{font.family}', sans-serif; font-display: swap;"
1845
+ style="font-family: '{font.family}', sans-serif; font-display: swap; color: inherit; -webkit-text-fill-color: currentColor;"
1828
1846
  >
1829
1847
  {@html highlightMatch(font.family, searchTerm)}
1830
1848
  </span>
@@ -1861,15 +1879,13 @@
1861
1879
  </Popover.Content>
1862
1880
  </Popover.Root>
1863
1881
  </div>
1864
- </div>
1865
- </div>
1882
+ </div>
1866
1883
 
1867
- <!-- Font Weight Section -->
1868
- <div class="flex items-center w-full relative h-[48px]">
1869
- <div class="w-[185px]"></div>
1870
- <div class="absolute right-0">
1871
- <div class="h-[48px] w-[185px] rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green">
1872
- <!-- Font Weight Button -->
1884
+ <div class="font-weight-row flex items-center justify-between w-full min-w-0 gap-2" style="min-height: 48px;">
1885
+ <div class="font-weight-label-ct flex-1 min-w-0">
1886
+ <InputControllerHeader label="Font Weight" />
1887
+ </div>
1888
+ <div class="font-weight-input-ct h-12 shrink-0 rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green" style="width: 185px; max-width: 185px; min-width: 185px; margin-left: auto;">
1873
1889
  <Popover.Root
1874
1890
  open={$openDropdowns?.has('weight-' + controllerId)}
1875
1891
  onOpenChange={(isOpen) => {
@@ -1880,33 +1896,33 @@
1880
1896
  >
1881
1897
  <Popover.Trigger class="w-full h-full flex items-center border-0 bg-transparent hover:bg-transparent focus:bg-transparent pl-3 pr-[16px] group">
1882
1898
  <span class="truncate flex-1 text-left subtext text-grey7">
1883
- {displayWeight}
1899
+ {displayWeight || 'Regular'}
1884
1900
  </span>
1885
1901
  <IconCaretDownFilled class="w-5 h-5 shrink-0 text-grey5 group-hover:text-grey7" />
1886
1902
  </Popover.Trigger>
1887
1903
  <Popover.Content class="p-0 w-[150px]">
1888
1904
  <Command.Root>
1889
- <Command.Group>
1890
- {#each availableWeights as weight}
1891
- <Command.Item
1892
- value={weight}
1893
- onSelect={() => handleWeightSelect(weight)}
1894
- class="px-2 py-2"
1895
- >
1896
- <Check
1897
- class="mr-1 h-4 w-4 {displayWeight === weight ? 'opacity-100' : 'opacity-0'}"
1898
- />
1899
- <span class="truncate">{weight}</span>
1900
- </Command.Item>
1901
- {/each}
1902
- </Command.Group>
1905
+ <Command.List>
1906
+ <Command.Group>
1907
+ {#each availableWeights as weight}
1908
+ <Command.Item
1909
+ value={weight}
1910
+ onSelect={() => handleWeightSelect(weight)}
1911
+ class="px-2 py-2"
1912
+ >
1913
+ <Check
1914
+ class="mr-1 h-4 w-4 {displayWeight === weight ? 'opacity-100' : 'opacity-0'}"
1915
+ />
1916
+ <span class="truncate">{weight}</span>
1917
+ </Command.Item>
1918
+ {/each}
1919
+ </Command.Group>
1920
+ </Command.List>
1903
1921
  </Command.Root>
1904
1922
  </Popover.Content>
1905
1923
  </Popover.Root>
1906
- </div>
1907
1924
  </div>
1908
1925
  </div>
1909
- </div>
1910
1926
 
1911
1927
  {#if description}
1912
1928
  <p class="subtext text-grey5">{description}</p>
@@ -1914,6 +1930,24 @@
1914
1930
  </div>
1915
1931
 
1916
1932
  <style>
1933
+ .font-family-row,
1934
+ .font-weight-row {
1935
+ display: flex;
1936
+ align-items: center;
1937
+ justify-content: space-between;
1938
+ width: 100%;
1939
+ }
1940
+
1941
+ .font-family-input-ct,
1942
+ .font-weight-input-ct {
1943
+ width: 185px !important;
1944
+ max-width: 185px !important;
1945
+ min-width: 185px !important;
1946
+ margin-left: auto;
1947
+ flex-shrink: 0;
1948
+ position: relative;
1949
+ }
1950
+
1917
1951
  /* Update button styles */
1918
1952
  :global(button.bg-transparent) {
1919
1953
  background: transparent !important;
@@ -1932,9 +1966,11 @@
1932
1966
  scrollbar-color: hsl(var(--grey3)) transparent;
1933
1967
  }
1934
1968
 
1935
- /* Ensure buttons take full width */
1936
- :global(.popover-trigger),
1937
- :global(.popover-trigger button) {
1969
+ /* Trigger fills the 185px column only — do not leak width:100% to other ICs */
1970
+ .font-family-input-ct :global(.popover-trigger),
1971
+ .font-family-input-ct :global(.popover-trigger button),
1972
+ .font-weight-input-ct :global(.popover-trigger),
1973
+ .font-weight-input-ct :global(.popover-trigger button) {
1938
1974
  width: 100% !important;
1939
1975
  height: 100% !important;
1940
1976
  }
@@ -28,7 +28,7 @@ declare const FontInputController: $$__sveltets_2_IsomorphicComponent<{
28
28
  family: string;
29
29
  weight: string;
30
30
  category: string;
31
- } | null;
31
+ } | string | null;
32
32
  onChange?: ((newValue: {
33
33
  family: string;
34
34
  weight: string;