@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.
@@ -69,7 +69,8 @@
69
69
  onChange,
70
70
  config = {},
71
71
  description = undefined,
72
- onFocus = () => {}
72
+ onFocus = () => {},
73
+ onClose = undefined
73
74
  } = $props<{
74
75
  class: string;
75
76
  label: string;
@@ -78,6 +79,7 @@
78
79
  config?: InputControllerConfig;
79
80
  description?: string;
80
81
  onFocus?: () => void;
82
+ onClose?: () => void;
81
83
  }>();
82
84
 
83
85
  // Use config values with fallbacks
@@ -320,14 +322,15 @@
320
322
 
321
323
  function handleEditorUpdate(e: any) {
322
324
  if (!editorInstance) return;
323
-
324
- const newContent = e.detail.html;
325
- if (newContent !== value) {
326
- content.set(newContent);
327
- onChange(newContent);
328
- updateActiveFormats();
329
- currentTextStyle = getCurrentTextStyle();
330
- }
325
+
326
+ // bind:content on ShadEditor already assigned `value` before this event,
327
+ // so comparing to `value` skipped onChange and the compact preview never updated.
328
+ const newContent = e.detail?.html ?? editorInstance.getHTML?.() ?? '';
329
+ lastKnownContent = newContent;
330
+ content.set(newContent);
331
+ onChange?.(newContent);
332
+ updateActiveFormats();
333
+ currentTextStyle = getCurrentTextStyle();
331
334
  }
332
335
 
333
336
  /**
@@ -1079,7 +1082,7 @@
1079
1082
  <!-- Toolbar at the top -->
1080
1083
  <div
1081
1084
  bind:this={toolbarRef}
1082
- class="toolbar-container sticky top-0 left-0 right-0 h-[56px] bg-grey1 z-10 border-b border-grey2 overflow-hidden select-none"
1085
+ class="toolbar-container sticky top-0 left-0 right-0 h-[56px] bg-grey1 z-10 border-b border-grey2 overflow-visible select-none"
1083
1086
  on:mousedown={handleToolbarInteraction}
1084
1087
  on:click={handleToolbarInteraction}
1085
1088
  on:keydown={handleToolbarKeyDown}
@@ -1087,18 +1090,17 @@
1087
1090
  aria-label="Text formatting toolbar"
1088
1091
  tabindex="-1"
1089
1092
  >
1090
- <div class="flex items-center flex-1 relative h-8">
1091
- <!-- Normal toolbar items -->
1092
- <div class={`toolbar-items-container flex items-center flex-1 absolute inset-0 transition-transform duration-&lsqb;${TRANSITION_DURATION}ms&rsqb; ease-out ${
1093
+ <div class="toolbar-items-row relative flex items-center h-full w-full">
1094
+ <!-- Format cluster centered; close stays on the right -->
1095
+ <div class={`toolbar-items-container absolute inset-y-0 left-0 right-10 flex items-center justify-center gap-2 transition-transform duration-200 ease-out ${
1093
1096
  isColorPickerAnimating ? 'translate-y-[-200%]' :
1094
1097
  showColorPicker ? 'translate-y-[-200%]' :
1095
1098
  'translate-y-0'
1096
1099
  }`}>
1097
- <!-- Color Picker Preview Button -->
1098
1100
  <Button
1099
1101
  variant="outline"
1100
- size="sm"
1101
- class="color-picker-preview-button toolbar-button h-9 w-9 p-0 relative overflow-hidden mr-2"
1102
+ size="icon"
1103
+ class="color-picker-preview-button toolbar-button !h-8 !w-8 !min-w-8 !p-0 shrink-0"
1102
1104
  onclick={(e) => {
1103
1105
  e.stopPropagation();
1104
1106
  e.preventDefault();
@@ -1120,7 +1122,7 @@
1120
1122
  type="multiple"
1121
1123
  size="sm"
1122
1124
  variant="outline"
1123
- class="text-formatting-toggle-group inline-flex h-8"
1125
+ class="text-formatting-toggle-group inline-flex h-8 gap-2"
1124
1126
  value={toggleValues}
1125
1127
  onValueChange={(value) => {
1126
1128
  if (!editorInstance) return;
@@ -1128,16 +1130,16 @@
1128
1130
  handleToggleChange(value);
1129
1131
  }}
1130
1132
  >
1131
- {#each Object.entries(formatMapping) as [format, { value, icon: Icon }]}
1133
+ {#each Object.entries(formatMapping) as [format, { value, icon: Icon }] (format)}
1132
1134
  <ToggleGroup.Item
1133
1135
  {value}
1134
1136
  aria-label={`Toggle ${format}`}
1135
1137
  data-state={activeFormats[format as FormatType] ? 'on' : 'off'}
1136
- class="toolbar-button {activeFormats[format as FormatType] ? 'bg-muted' : ''} {format !== 'link' ? 'mr-2' : ''}"
1138
+ class="toolbar-button shrink-0 !h-8 !w-8 !min-w-8 !p-0 {activeFormats[format as FormatType] ? 'bg-muted' : ''}"
1137
1139
  >
1138
1140
  <div class="toolbar-button-content flex h-full w-full items-center justify-center">
1139
1141
  <Icon
1140
- class="!text-grey5 hover:!text-grey7"
1142
+ class="text-current"
1141
1143
  style="width: 20px !important; height: 20px !important; min-width: 20px !important; min-height: 20px !important; max-width: 20px !important; max-height: 20px !important;"
1142
1144
  />
1143
1145
  </div>
@@ -1146,7 +1148,7 @@
1146
1148
  </ToggleGroup.Root>
1147
1149
 
1148
1150
  <!-- Text Style Dropdown -->
1149
- <div class="pl-2">
1151
+ <div class="toolbar-text-style-ct inline-flex items-center shrink-0">
1150
1152
  <Popover.Root
1151
1153
  bind:open={textStyleDropdownOpen}
1152
1154
  onOpenChange={(e: any) => {
@@ -1159,8 +1161,8 @@
1159
1161
  bind:this={triggerButton}
1160
1162
  id="text-style-trigger"
1161
1163
  variant="outline"
1162
- size="sm"
1163
- class="toolbar-button h-8 w-[54px] justify-between"
1164
+ size="icon"
1165
+ class="toolbar-button toolbar-dropdown-trigger !h-8 !w-11 !min-w-11 !max-w-11 !p-0 justify-center"
1164
1166
  onclick={(e) => {
1165
1167
  e.stopPropagation();
1166
1168
  e.preventDefault();
@@ -1172,13 +1174,13 @@
1172
1174
  textStyleDropdownOpen = !textStyleDropdownOpen;
1173
1175
  }}
1174
1176
  >
1175
- <div class="toolbar-button-content flex h-full w-full items-center justify-between px-2">
1177
+ <div class="toolbar-button-content flex h-full w-full items-center justify-center gap-0.5 px-1">
1176
1178
  {#if currentTextStyle === 'text'}
1177
- <IconPilcrow class="h-4 w-4" />
1179
+ <IconPilcrow class="h-3.5 w-3.5 shrink-0" />
1178
1180
  {:else}
1179
- <span>{textOptions.find(opt => opt.value === currentTextStyle)?.label || 'Text'}</span>
1181
+ <span class="text-xs">{textOptions.find(opt => opt.value === currentTextStyle)?.label || 'Text'}</span>
1180
1182
  {/if}
1181
- <ChevronDown class="h-4 w-4" />
1183
+ <ChevronDown class="h-3 w-3 shrink-0" />
1182
1184
  </div>
1183
1185
  </Button>
1184
1186
  </Popover.Trigger>
@@ -1220,7 +1222,7 @@
1220
1222
  </div>
1221
1223
 
1222
1224
  <!-- List Style Dropdown -->
1223
- <div class="pl-2">
1225
+ <div class="toolbar-list-style-ct inline-flex items-center shrink-0">
1224
1226
  <Popover.Root
1225
1227
  bind:open={listStyleDropdownOpen}
1226
1228
  onOpenChange={(e: any) => {
@@ -1231,8 +1233,9 @@
1231
1233
  <Popover.Trigger>
1232
1234
  <Button
1233
1235
  variant="outline"
1234
- size="sm"
1235
- class="toolbar-button h-8 w-[54px] justify-between"
1236
+ size="icon"
1237
+ id="list-style-trigger"
1238
+ class="toolbar-button toolbar-dropdown-trigger !h-8 !w-11 !min-w-11 !max-w-11 !p-0 justify-center"
1236
1239
  onclick={(e) => {
1237
1240
  e.stopPropagation();
1238
1241
  e.preventDefault();
@@ -1241,16 +1244,16 @@
1241
1244
  listStyleDropdownOpen = !listStyleDropdownOpen;
1242
1245
  }}
1243
1246
  >
1244
- <div class="toolbar-button-content flex h-full w-full items-center justify-between px-2">
1247
+ <div class="toolbar-button-content flex h-full w-full items-center justify-center gap-0.5 px-1">
1245
1248
  {#if currentTextStyle && currentTextStyle.includes('List')}
1246
1249
  {@const activeListOption = listOptions.find(opt => opt.value === currentTextStyle)}
1247
1250
  {#if activeListOption?.icon}
1248
- <activeListOption.icon class="h-4 w-4" />
1251
+ <activeListOption.icon class="h-3.5 w-3.5 shrink-0" />
1249
1252
  {/if}
1250
1253
  {:else}
1251
- <IconList class="h-4 w-4" />
1254
+ <IconList class="h-3.5 w-3.5 shrink-0" />
1252
1255
  {/if}
1253
- <ChevronDown class="h-4 w-4" />
1256
+ <ChevronDown class="h-3 w-3 shrink-0" />
1254
1257
  </div>
1255
1258
  </Button>
1256
1259
  </Popover.Trigger>
@@ -1288,17 +1291,30 @@
1288
1291
  </div>
1289
1292
  </div>
1290
1293
 
1291
- <!-- Color picker panel -->
1292
- <div class={`color-picker-parent flex items-center gap-2 absolute inset-0 transition-transform duration-&lsqb;${TRANSITION_DURATION}ms&rsqb; ease-out ${
1294
+ {#if onClose && !showColorPicker && !isColorPickerAnimating}
1295
+ <Button
1296
+ variant="ghost"
1297
+ class="toolbar-close-button ml-auto shrink-0 p-0 hover:bg-transparent h-8 w-8 mr-2 z-10"
1298
+ onclick={(e) => {
1299
+ e.stopPropagation();
1300
+ e.preventDefault();
1301
+ onClose();
1302
+ }}
1303
+ >
1304
+ <X class="h-6 w-6 text-grey5 hover:text-grey7" />
1305
+ </Button>
1306
+ {/if}
1307
+
1308
+ {#if showColorPicker || isColorPickerAnimating}
1309
+ <div class={`color-picker-parent flex items-center gap-2 absolute inset-0 z-20 bg-grey1 px-3 transition-transform duration-200 ease-out ${
1293
1310
  isColorPickerAnimating ? 'translate-y-0' :
1294
1311
  showColorPicker ? 'translate-y-0' :
1295
1312
  'translate-y-[200%]'
1296
1313
  }`}>
1297
- <!-- Close Button First -->
1298
1314
  <Button
1299
1315
  variant="outline"
1300
- size="sm"
1301
- class="color-picker-close-button toolbar-button h-9 w-9 p-0 ml-4"
1316
+ size="icon"
1317
+ class="color-picker-close-button toolbar-button !h-8 !w-8 !p-0 shrink-0"
1302
1318
  onclick={(e) => {
1303
1319
  e.stopPropagation();
1304
1320
  e.preventDefault();
@@ -1312,8 +1328,7 @@
1312
1328
  </div>
1313
1329
  </Button>
1314
1330
 
1315
- <!-- Color Picker Container -->
1316
- <div class="flex-1">
1331
+ <div class="color-picker-toolbar-slot relative w-[185px] shrink-0 overflow-visible">
1317
1332
  <ColorPickerInputController
1318
1333
  bind:value={currentColor}
1319
1334
  onChange={(color) => {
@@ -1346,11 +1361,12 @@
1346
1361
  />
1347
1362
  </div>
1348
1363
  </div>
1364
+ {/if}
1349
1365
  </div>
1350
1366
  </div>
1351
1367
 
1352
1368
  <!-- Scrollable content area -->
1353
- <div class="editor-content-area h-[calc(100%-56px)] overflow-auto">
1369
+ <div class="editor-content-area min-h-0 flex-1 overflow-y-auto overflow-x-hidden">
1354
1370
  <ShadEditor
1355
1371
  bind:content={value}
1356
1372
  showToolbar={false}
@@ -1369,17 +1385,18 @@
1369
1385
  <style>
1370
1386
 
1371
1387
  /* Update editor styles */
1372
- :global(.ProseMirror) {
1388
+ :global(.text-area-input-controller .ProseMirror) {
1373
1389
  outline: none !important;
1374
1390
  width: 100%;
1375
- height: calc(100% - 56px);
1391
+ min-height: 100%;
1392
+ height: auto;
1376
1393
  padding: var(--editor-padding-top, 0.35rem) var(--editor-padding-right, 0.35rem)
1377
1394
  var(--editor-padding-bottom, 0.35rem) var(--editor-padding-left, 0.35rem);
1378
1395
  background: transparent !important;
1379
1396
  border: none !important;
1380
1397
  border-radius: inherit;
1381
- overflow-y: auto;
1382
- margin-top: 56px;
1398
+ overflow: visible;
1399
+ margin-top: 0;
1383
1400
  scrollbar-width: thin;
1384
1401
  scrollbar-color: transparent transparent;
1385
1402
  }
@@ -1493,6 +1510,14 @@
1493
1510
  }
1494
1511
 
1495
1512
  /* Toolbar styles - Consolidated */
1513
+ :global(.toolbar-container button:focus),
1514
+ :global(.toolbar-container button:focus-visible),
1515
+ :global(.toolbar-container a:focus),
1516
+ :global(.toolbar-container a:focus-visible) {
1517
+ outline: none;
1518
+ box-shadow: none;
1519
+ }
1520
+
1496
1521
  :global(.toolbar-button) {
1497
1522
  position: relative !important;
1498
1523
  border: 1px solid hsl(var(--border)) !important;
@@ -1517,6 +1542,47 @@
1517
1542
  z-index: 2 !important;
1518
1543
  }
1519
1544
 
1545
+ :global(.toolbar-text-style-ct),
1546
+ :global(.toolbar-list-style-ct) {
1547
+ width: 44px;
1548
+ min-width: 44px;
1549
+ max-width: 44px;
1550
+ flex-shrink: 0;
1551
+ }
1552
+
1553
+ :global(.text-formatting-toggle-group button),
1554
+ :global(.color-picker-preview-button) {
1555
+ width: 2rem !important;
1556
+ min-width: 2rem !important;
1557
+ max-width: 2rem !important;
1558
+ height: 2rem !important;
1559
+ padding: 0 !important;
1560
+ }
1561
+
1562
+ :global(.toolbar-text-style-ct button),
1563
+ :global(.toolbar-list-style-ct button),
1564
+ :global(.toolbar-dropdown-trigger) {
1565
+ width: 44px !important;
1566
+ min-width: 44px !important;
1567
+ max-width: 44px !important;
1568
+ height: 2rem !important;
1569
+ padding: 0 !important;
1570
+ }
1571
+
1572
+ :global(.text-area-input-controller .editor-content-area) {
1573
+ scrollbar-width: thin;
1574
+ scrollbar-color: hsl(var(--grey3) / 0.5) transparent;
1575
+ }
1576
+
1577
+ :global(.text-area-input-controller .editor-content-area::-webkit-scrollbar) {
1578
+ width: 6px;
1579
+ }
1580
+
1581
+ :global(.text-area-input-controller .editor-content-area::-webkit-scrollbar-thumb) {
1582
+ background: hsl(var(--grey3) / 0.45);
1583
+ border-radius: 999px;
1584
+ }
1585
+
1520
1586
  /* Input styles */
1521
1587
  :global(input.h-8),
1522
1588
  :global(.input-container) {
@@ -1671,8 +1737,11 @@
1671
1737
  }
1672
1738
 
1673
1739
  :global(.text-area .toolbar-container button) {
1674
- width: 36px !important;
1675
- height: 36px !important;
1740
+ width: 32px !important;
1741
+ height: 32px !important;
1742
+ min-width: 32px !important;
1743
+ max-width: 32px !important;
1744
+ padding: 0 !important;
1676
1745
  background-color: white !important;
1677
1746
  border: 1px solid hsl(var(--grey2)) !important;
1678
1747
  border-radius: var(--radius) !important;
@@ -1682,6 +1751,29 @@
1682
1751
  transition: all 0.2s !important;
1683
1752
  }
1684
1753
 
1754
+ :global(.text-area .toolbar-container button.toolbar-dropdown-trigger),
1755
+ :global(.text-area .toolbar-container #text-style-trigger),
1756
+ :global(.text-area .toolbar-container #list-style-trigger) {
1757
+ width: 44px !important;
1758
+ min-width: 44px !important;
1759
+ max-width: 44px !important;
1760
+ padding: 0 !important;
1761
+ }
1762
+
1763
+ :global(.text-area .toolbar-container .toolbar-items-container) {
1764
+ display: flex !important;
1765
+ align-items: center !important;
1766
+ justify-content: center !important;
1767
+ gap: 8px !important;
1768
+ height: 100% !important;
1769
+ position: absolute !important;
1770
+ inset: 0 2.5rem 0 0 !important;
1771
+ }
1772
+
1773
+ :global(.text-area #text-style-trigger) {
1774
+ margin-right: 0 !important;
1775
+ }
1776
+
1685
1777
  /* Color the brush icon based on selected color */
1686
1778
  :global(.text-area .color-picker-preview-button svg) {
1687
1779
  color: var(--selected-color, currentColor) !important;
@@ -1692,14 +1784,14 @@
1692
1784
  }
1693
1785
  /* Button spacing */
1694
1786
  :global(.text-area .color-picker-preview-button) {
1695
- margin-left: 16px !important;
1696
- margin-right: 8px !important;
1787
+ margin-left: 0 !important;
1788
+ margin-right: 0 !important;
1697
1789
  }
1698
1790
 
1699
1791
  :global(.text-area .text-formatting-toggle-group) {
1700
- margin-right: 8px !important;
1792
+ margin-right: 0 !important;
1701
1793
  display: flex !important;
1702
- gap: 0 !important;
1794
+ gap: 8px !important;
1703
1795
  }
1704
1796
 
1705
1797
  :global(.text-area .text-formatting-toggle-group button) {
@@ -1707,11 +1799,14 @@
1707
1799
  }
1708
1800
 
1709
1801
  :global(.text-area .text-formatting-toggle-group button:not(:last-child)) {
1710
- margin-right: 8px !important;
1802
+ margin-right: 0 !important;
1711
1803
  }
1712
1804
 
1713
- :global(.text-area #text-style-trigger) {
1714
- width: 54px !important;
1805
+ :global(.text-area #text-style-trigger),
1806
+ :global(.text-area #list-style-trigger) {
1807
+ width: 44px !important;
1808
+ min-width: 44px !important;
1809
+ max-width: 44px !important;
1715
1810
  }
1716
1811
 
1717
1812
  :global(.text-area .toolbar-container button:hover) {
@@ -1733,14 +1828,21 @@
1733
1828
  color: hsl(var(--grey7)) !important;
1734
1829
  }
1735
1830
 
1736
- /* Style for active state */
1737
1831
  :global(.text-area .toolbar-container button[data-state="on"]) {
1738
1832
  background-color: hsl(var(--sage-green)) !important;
1739
- border-color: hsl(var(--grey1)) !important;
1833
+ border-color: hsl(var(--sage-green)) !important;
1834
+ color: white !important;
1740
1835
  }
1741
1836
 
1742
- :global(.text-area .toolbar-container button[data-state="on"] svg) {
1837
+ :global(.text-area .toolbar-container button[data-state="on"] svg),
1838
+ :global(.text-area .toolbar-container button[data-state="on"] svg *) {
1743
1839
  color: white !important;
1840
+ stroke: white !important;
1841
+ }
1842
+
1843
+ :global(.text-area .toolbar-text-style-ct),
1844
+ :global(.text-area .toolbar-list-style-ct) {
1845
+ margin-left: 0 !important;
1744
1846
  }
1745
1847
 
1746
1848
  /* Update the color picker button to show selected color */
@@ -1771,21 +1873,47 @@
1771
1873
  cursor: text;
1772
1874
  }
1773
1875
 
1774
- /* Add width for list style dropdown button */
1775
- :global(.text-area .toolbar-button.h-8) {
1776
- width: 54px !important;
1876
+ :global(.text-area .toolbar-button.h-8:not(.toolbar-dropdown-trigger):not(#text-style-trigger):not(#list-style-trigger)) {
1877
+ width: 32px !important;
1878
+ }
1879
+
1880
+ :global(.text-area .toolbar-text-style-ct),
1881
+ :global(.text-area .toolbar-list-style-ct) {
1882
+ display: inline-flex !important;
1883
+ width: 44px !important;
1884
+ min-width: 44px !important;
1885
+ max-width: 44px !important;
1886
+ margin-left: 0 !important;
1887
+ padding-left: 0 !important;
1777
1888
  }
1778
1889
 
1779
1890
  /* Update color picker container styles */
1780
1891
  :global(.color-picker-parent) {
1781
1892
  position: absolute;
1782
1893
  inset: 0;
1783
- padding: 0;
1894
+ padding: 0 12px;
1784
1895
  display: flex;
1785
1896
  align-items: center;
1897
+ justify-content: flex-start;
1786
1898
  gap: 8px;
1787
1899
  }
1788
1900
 
1901
+ /* ColorPicker uses absolute right-0 for the Design column; in the toolbar that
1902
+ pins the 185px input to the far right and the popover flush to the edge. */
1903
+ :global(.color-picker-toolbar-slot .input-controller-ct) {
1904
+ position: relative !important;
1905
+ right: auto !important;
1906
+ left: auto !important;
1907
+ }
1908
+
1909
+ :global(.color-picker-toolbar-slot .input-controller-header-ct) {
1910
+ display: none !important;
1911
+ }
1912
+
1913
+ :global(.color-picker-toolbar-slot .color-picker-input-controller-ct > .flex) {
1914
+ display: block !important;
1915
+ }
1916
+
1789
1917
  :global(.color-picker-parent input[type="text"]) {
1790
1918
  width: 162px !important;
1791
1919
  min-width: 162px !important;
@@ -7,6 +7,7 @@ type $$ComponentProps = {
7
7
  config?: InputControllerConfig;
8
8
  description?: string;
9
9
  onFocus?: () => void;
10
+ onClose?: () => void;
10
11
  };
11
12
  declare const TextAreaInputController: import("svelte").Component<$$ComponentProps, {}, "value">;
12
13
  type TextAreaInputController = ReturnType<typeof TextAreaInputController>;