kritzel-stencil 0.1.0 → 0.1.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.
Files changed (176) hide show
  1. package/dist/cjs/{default-line-tool.config-7eJND6Jb.js → default-line-tool.config-MA02HCrH.js} +703 -133
  2. package/dist/cjs/{index-BeKMS-Zt.js → index-Bj0n7fQQ.js} +84 -7
  3. package/dist/cjs/index.cjs.js +1 -1
  4. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  5. package/dist/cjs/{kritzel-color_22.cjs.entry.js → kritzel-color_24.cjs.entry.js} +1018 -897
  6. package/dist/cjs/loader.cjs.js +2 -2
  7. package/dist/cjs/stencil.cjs.js +3 -3
  8. package/dist/collection/classes/core/core.class.js +2 -0
  9. package/dist/collection/classes/core/viewport.class.js +43 -3
  10. package/dist/collection/classes/handlers/move.handler.js +6 -0
  11. package/dist/collection/classes/objects/line.class.js +63 -15
  12. package/dist/collection/classes/objects/path.class.js +1 -0
  13. package/dist/collection/classes/objects/shape.class.js +1 -0
  14. package/dist/collection/classes/objects/text.class.js +4 -3
  15. package/dist/collection/classes/providers/indexeddb-sync-provider.class.js +0 -1
  16. package/dist/collection/classes/tools/brush-tool.class.js +5 -0
  17. package/dist/collection/classes/tools/line-tool.class.js +31 -1
  18. package/dist/collection/classes/tools/selection-tool.class.js +193 -0
  19. package/dist/collection/classes/tools/shape-tool.class.js +2 -0
  20. package/dist/collection/classes/tools/text-tool.class.js +3 -0
  21. package/dist/collection/collection-manifest.json +5 -3
  22. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +3 -2
  23. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +37 -19
  24. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +108 -36
  25. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  26. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.css +1 -1
  27. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +24 -2
  28. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  29. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.css +1 -1
  30. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.css +1 -1
  31. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  32. package/dist/collection/components/shared/kritzel-line-endings/kritzel-line-endings.css +60 -0
  33. package/dist/collection/components/shared/kritzel-line-endings/kritzel-line-endings.js +187 -0
  34. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +15 -8
  35. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +16 -9
  36. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.css +85 -0
  37. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +163 -0
  38. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  39. package/dist/collection/components/shared/kritzel-shape-fill/kritzel-shape-fill.css +47 -0
  40. package/dist/collection/components/shared/kritzel-shape-fill/kritzel-shape-fill.js +93 -0
  41. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +13 -7
  42. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.css +11 -2
  43. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +2 -2
  44. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.css +1 -1
  45. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +6 -4
  46. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +6 -3
  47. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +66 -0
  48. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +153 -50
  49. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.css +38 -0
  50. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.js +321 -0
  51. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +3 -2
  52. package/dist/collection/components/ui/kritzel-workspace-manager/kritzel-workspace-manager.js +6 -3
  53. package/dist/collection/configs/default-brush-tool.config.js +2 -52
  54. package/dist/collection/configs/default-line-tool.config.js +2 -26
  55. package/dist/collection/configs/default-shape-tool.config.js +2 -15
  56. package/dist/collection/configs/default-text-tool.config.js +2 -26
  57. package/dist/collection/constants/color-palette.constants.js +30 -0
  58. package/dist/collection/helpers/color.helper.js +31 -0
  59. package/dist/collection/helpers/tool-config.helper.js +65 -0
  60. package/dist/collection/interfaces/tool-config.interface.js +1 -0
  61. package/dist/components/index.d.ts +8 -4
  62. package/dist/components/index.js +1 -1
  63. package/dist/components/kritzel-brush-style.js +1 -1
  64. package/dist/components/kritzel-color-palette.js +1 -1
  65. package/dist/components/kritzel-color.js +1 -1
  66. package/dist/components/kritzel-context-menu.js +1 -1
  67. package/dist/components/kritzel-controls.js +1 -1
  68. package/dist/components/kritzel-cursor-trail.js +1 -1
  69. package/dist/components/kritzel-dropdown.js +1 -1
  70. package/dist/components/kritzel-editor.js +1 -1
  71. package/dist/components/kritzel-engine.js +1 -1
  72. package/dist/components/kritzel-font-family.js +1 -1
  73. package/dist/components/kritzel-font-size.js +1 -1
  74. package/dist/components/kritzel-font.js +1 -1
  75. package/dist/components/kritzel-icon.js +1 -1
  76. package/dist/components/kritzel-line-endings.d.ts +11 -0
  77. package/dist/components/kritzel-line-endings.js +1 -0
  78. package/dist/components/kritzel-menu-item.js +1 -1
  79. package/dist/components/kritzel-menu.js +1 -1
  80. package/dist/components/kritzel-opacity-slider.d.ts +11 -0
  81. package/dist/components/kritzel-opacity-slider.js +1 -0
  82. package/dist/components/kritzel-portal.js +1 -1
  83. package/dist/components/kritzel-shape-fill.d.ts +11 -0
  84. package/dist/components/kritzel-shape-fill.js +1 -0
  85. package/dist/components/kritzel-split-button.js +1 -1
  86. package/dist/components/kritzel-stroke-size.js +1 -1
  87. package/dist/components/kritzel-tool-config.d.ts +11 -0
  88. package/dist/components/kritzel-tool-config.js +1 -0
  89. package/dist/components/kritzel-tooltip.js +1 -1
  90. package/dist/components/kritzel-utility-panel.js +1 -1
  91. package/dist/components/kritzel-workspace-manager.js +1 -1
  92. package/dist/components/p-83YX0-FS.js +1 -0
  93. package/dist/components/p-8iEiCuEN.js +1 -0
  94. package/dist/components/p-9XZbc_qK.js +1 -0
  95. package/dist/components/p-B3P64-gH.js +9 -0
  96. package/dist/components/p-B8QjTqOY.js +1 -0
  97. package/dist/components/p-BF6MdW17.js +1 -0
  98. package/dist/components/p-BVIY50lR.js +1 -0
  99. package/dist/components/p-BbqT9o1F.js +1 -0
  100. package/dist/components/{p-CXzfYQ_u.js → p-BnidlyU0.js} +1 -1
  101. package/dist/components/{p-Bj_Og27M.js → p-BxS4Pdpz.js} +1 -1
  102. package/dist/components/{p-g0N9j_uT.js → p-CCj8nmQH.js} +1 -1
  103. package/dist/components/{p-1z-ds26_.js → p-CLOnpu42.js} +1 -1
  104. package/dist/components/{p-D1tfzpy8.js → p-CSGeDE4f.js} +1 -1
  105. package/dist/components/p-CbuHMNa9.js +1 -0
  106. package/dist/components/p-ClMFs3KI.js +1 -0
  107. package/dist/components/{p-IAqZFssU.js → p-Cnpk2hfo.js} +1 -1
  108. package/dist/components/{p-Cy77SpWt.js → p-Ctv4NAxk.js} +1 -1
  109. package/dist/components/p-CyHZWbkS.js +1 -0
  110. package/dist/components/{p-C4krHoUl.js → p-D8GeJNUv.js} +1 -1
  111. package/dist/components/{p-DB5s1NY4.js → p-DKgqzi2Y.js} +1 -1
  112. package/dist/components/p-DOF5fWDU.js +1 -0
  113. package/dist/components/{p-4FEa4ADy.js → p-DV_h5Jo2.js} +1 -1
  114. package/dist/components/{p-DTezr6w9.js → p-DgCGSL2Q.js} +1 -1
  115. package/dist/components/{p-D5ZsALCP.js → p-wRXL928z.js} +1 -1
  116. package/dist/esm/{default-line-tool.config-CD5sTKH-.js → default-line-tool.config-DLpNl6R9.js} +702 -125
  117. package/dist/esm/{index-BqhmuUH2.js → index-OLdaFN6W.js} +84 -7
  118. package/dist/esm/index.js +2 -2
  119. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  120. package/dist/esm/{kritzel-color_22.entry.js → kritzel-color_24.entry.js} +1009 -890
  121. package/dist/esm/loader.js +3 -3
  122. package/dist/esm/stencil.js +4 -4
  123. package/dist/stencil/index.esm.js +1 -1
  124. package/dist/stencil/{p-09295079.entry.js → p-802bc7cf.entry.js} +1 -1
  125. package/dist/stencil/p-DLpNl6R9.js +1 -0
  126. package/dist/stencil/p-OLdaFN6W.js +2 -0
  127. package/dist/stencil/p-caf30edb.entry.js +9 -0
  128. package/dist/stencil/stencil.esm.js +1 -1
  129. package/dist/types/classes/core/viewport.class.d.ts +6 -0
  130. package/dist/types/classes/managers/anchor.manager.d.ts +1 -1
  131. package/dist/types/classes/objects/line.class.d.ts +2 -0
  132. package/dist/types/classes/objects/shape.class.d.ts +1 -0
  133. package/dist/types/classes/tools/brush-tool.class.d.ts +1 -0
  134. package/dist/types/classes/tools/line-tool.class.d.ts +2 -1
  135. package/dist/types/classes/tools/selection-tool.class.d.ts +22 -0
  136. package/dist/types/classes/tools/shape-tool.class.d.ts +1 -0
  137. package/dist/types/classes/tools/text-tool.class.d.ts +1 -0
  138. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -0
  139. package/dist/types/components/shared/kritzel-color-palette/kritzel-color-palette.d.ts +1 -0
  140. package/dist/types/components/shared/kritzel-line-endings/kritzel-line-endings.d.ts +23 -0
  141. package/dist/types/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.d.ts +17 -0
  142. package/dist/types/components/shared/kritzel-shape-fill/kritzel-shape-fill.d.ts +10 -0
  143. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +15 -0
  144. package/dist/types/components/ui/kritzel-tool-config/kritzel-tool-config.d.ts +25 -0
  145. package/dist/types/components.d.ts +235 -82
  146. package/dist/types/constants/color-palette.constants.d.ts +5 -0
  147. package/dist/types/helpers/color.helper.d.ts +9 -0
  148. package/dist/types/helpers/tool-config.helper.d.ts +4 -0
  149. package/dist/types/interfaces/line-options.interface.d.ts +1 -0
  150. package/dist/types/interfaces/path-options.interface.d.ts +1 -0
  151. package/dist/types/interfaces/tool-config.interface.d.ts +26 -0
  152. package/dist/types/stencil-public-runtime.d.ts +29 -0
  153. package/package.json +5 -3
  154. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.css +0 -19
  155. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +0 -134
  156. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.css +0 -19
  157. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +0 -114
  158. package/dist/components/kritzel-control-brush-config.d.ts +0 -11
  159. package/dist/components/kritzel-control-brush-config.js +0 -1
  160. package/dist/components/kritzel-control-text-config.d.ts +0 -11
  161. package/dist/components/kritzel-control-text-config.js +0 -1
  162. package/dist/components/p-B7Fdo5QJ.js +0 -1
  163. package/dist/components/p-BXaWhpO2.js +0 -1
  164. package/dist/components/p-BtuXeItZ.js +0 -1
  165. package/dist/components/p-C-d2IH4v.js +0 -1
  166. package/dist/components/p-C3UriJh7.js +0 -1
  167. package/dist/components/p-CF5L2Gdl.js +0 -1
  168. package/dist/components/p-CeKT_dTd.js +0 -1
  169. package/dist/components/p-Cp15toXH.js +0 -1
  170. package/dist/components/p-D3LRBk2t.js +0 -9
  171. package/dist/components/p-Du1vxHy8.js +0 -1
  172. package/dist/stencil/p-381c0e9c.entry.js +0 -9
  173. package/dist/stencil/p-BqhmuUH2.js +0 -2
  174. package/dist/stencil/p-CD5sTKH-.js +0 -1
  175. package/dist/types/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.d.ts +0 -15
  176. package/dist/types/components/ui/kritzel-control-text-config/kritzel-control-text-config.d.ts +0 -12
@@ -1,7 +1,12 @@
1
1
  import { h, Host } from "@stencil/core";
2
2
  import { KritzelBrushTool } from "../../../classes/tools/brush-tool.class";
3
+ import { KritzelLineTool } from "../../../classes/tools/line-tool.class";
4
+ import { KritzelShapeTool } from "../../../classes/tools/shape-tool.class";
3
5
  import { KritzelTextTool } from "../../../classes/tools/text-tool.class";
6
+ import { KritzelSelectionTool } from "../../../classes/tools/selection-tool.class";
4
7
  import { KritzelDevicesHelper } from "../../../helpers/devices.helper";
8
+ import { KritzelToolConfigHelper } from "../../../helpers/tool-config.helper";
9
+ import { KritzelColorHelper } from "../../../helpers/color.helper";
5
10
  export class KritzelControls {
6
11
  host;
7
12
  controls = [];
@@ -14,9 +19,23 @@ export class KritzelControls {
14
19
  isTouchDevice = KritzelDevicesHelper.isTouchDevice();
15
20
  selectedSubOptions = new Map();
16
21
  openSubMenuControl = null;
22
+ canScrollLeft = false;
23
+ canScrollRight = false;
24
+ displayValues = null;
17
25
  handleDocumentClick(event) {
18
- const element = event.target;
19
- if (!this.kritzelEngine || element.closest('.kritzel-tooltip')) {
26
+ if (!this.kritzelEngine) {
27
+ return;
28
+ }
29
+ // Use composedPath to check if click is inside tooltip (works across shadow DOM boundaries)
30
+ const path = event.composedPath();
31
+ const isInsideTooltip = path.some(el => {
32
+ const element = el;
33
+ if (element.tagName) {
34
+ return element.tagName.toLowerCase() === 'kritzel-tooltip' || element.classList?.contains('kritzel-tooltip');
35
+ }
36
+ return false;
37
+ });
38
+ if (isInsideTooltip) {
20
39
  return;
21
40
  }
22
41
  this.isTooltipVisible = false;
@@ -31,23 +50,87 @@ export class KritzelControls {
31
50
  }
32
51
  async handleActiveToolChange(event) {
33
52
  this.activeControl = this.controls.find(control => control.tool === event.detail) || null;
53
+ if (this.activeControl?.tool) {
54
+ this.updateDisplayValues(this.activeControl.tool);
55
+ }
56
+ }
57
+ handleSelectionChange() {
58
+ if (this.activeControl?.tool instanceof KritzelSelectionTool) {
59
+ this.updateDisplayValues(this.activeControl.tool);
60
+ }
34
61
  }
35
62
  async closeTooltip() {
36
63
  this.isTooltipVisible = false;
37
64
  }
38
65
  kritzelEngine = null;
39
66
  tooltipRef = null;
67
+ toolsScrollRef = null;
40
68
  get activeToolAsTextTool() {
41
69
  return this.activeControl?.tool;
42
70
  }
43
71
  get activeToolAsBrushTool() {
44
72
  return this.activeControl?.tool;
45
73
  }
74
+ get activeToolAsLineTool() {
75
+ return this.activeControl?.tool;
76
+ }
77
+ get activeToolAsShapeTool() {
78
+ return this.activeControl?.tool;
79
+ }
80
+ handleDisplayValuesChange = (event) => {
81
+ const newVal = event.detail;
82
+ if (this.displayValues &&
83
+ this.displayValues.color === newVal.color &&
84
+ this.displayValues.size === newVal.size &&
85
+ this.displayValues.fontFamily === newVal.fontFamily) {
86
+ return;
87
+ }
88
+ this.displayValues = newVal;
89
+ };
90
+ updateDisplayValues(tool) {
91
+ const config = KritzelToolConfigHelper.getToolConfig(tool);
92
+ if (!config) {
93
+ this.displayValues = null;
94
+ return;
95
+ }
96
+ const color = tool[config.colorProperty];
97
+ const opacity = tool[config.opacityProperty] ?? 1;
98
+ const size = tool[config.sizeProperty];
99
+ const displayValues = {
100
+ color: KritzelColorHelper.applyOpacity(color, opacity),
101
+ size,
102
+ };
103
+ if (tool instanceof KritzelTextTool) {
104
+ displayValues.fontFamily = tool.fontFamily;
105
+ }
106
+ // Check for equality implementation to prevent unnecessary re-renders
107
+ if (this.displayValues &&
108
+ this.displayValues.color === displayValues.color &&
109
+ this.displayValues.size === displayValues.size &&
110
+ this.displayValues.fontFamily === displayValues.fontFamily) {
111
+ return;
112
+ }
113
+ this.displayValues = displayValues;
114
+ }
46
115
  async componentWillLoad() {
47
116
  await this.initializeEngine();
48
117
  await this.initializeTools();
49
118
  this.isControlsReady.emit();
50
119
  }
120
+ componentDidLoad() {
121
+ this.updateScrollIndicators();
122
+ }
123
+ updateScrollIndicators() {
124
+ if (!this.toolsScrollRef)
125
+ return;
126
+ const { scrollLeft, scrollWidth, clientWidth } = this.toolsScrollRef;
127
+ const threshold = 2; // Small threshold to account for rounding
128
+ this.canScrollLeft = scrollLeft > threshold;
129
+ this.canScrollRight = scrollLeft + clientWidth < scrollWidth - threshold;
130
+ }
131
+ handleToolsScroll = () => {
132
+ this.updateScrollIndicators();
133
+ };
51
134
  async initializeEngine() {
52
135
  await customElements.whenDefined('kritzel-engine');
53
136
  this.kritzelEngine = this.host.parentElement.querySelector('kritzel-engine');
@@ -63,6 +146,7 @@ export class KritzelControls {
63
146
  if (c.type === 'tool' && c.isDefault && c.tool) {
64
147
  await this.kritzelEngine.changeActiveTool(c.tool);
65
148
  this.activeControl = c;
149
+ this.updateDisplayValues(c.tool);
66
150
  }
67
151
  if (c.type === 'config') {
68
152
  if (this.firstConfig === null) {
@@ -77,6 +161,7 @@ export class KritzelControls {
77
161
  async handleControlClick(control) {
78
162
  this.activeControl = control;
79
163
  if (this.activeControl.type === 'tool') {
164
+ this.updateDisplayValues(this.activeControl.tool);
80
165
  await this.kritzelEngine.changeActiveTool(this.activeControl.tool);
81
166
  }
82
167
  }
@@ -141,56 +226,62 @@ export class KritzelControls {
141
226
  }
142
227
  render() {
143
228
  const hasConfigUI = this.activeControl?.tool instanceof KritzelBrushTool ||
144
- this.activeControl?.tool instanceof KritzelTextTool;
145
- return (h(Host, { key: 'ed5816cbeb8fe8b91e14b23bf61df4753fb0da0f', class: {
229
+ this.activeControl?.tool instanceof KritzelTextTool ||
230
+ this.activeControl?.tool instanceof KritzelLineTool ||
231
+ this.activeControl?.tool instanceof KritzelShapeTool ||
232
+ (this.activeControl?.tool instanceof KritzelSelectionTool && this.activeControl.tool.hasSelection());
233
+ // Separate tool controls from config control
234
+ const toolControls = this.controls.filter(c => c.type === 'tool');
235
+ const configControl = this.controls.find(c => c.type === 'config' && c.name === this.firstConfig?.name);
236
+ return (h(Host, { key: 'db2a043a2a32d10d7f27c01123da63115781941c', class: {
146
237
  mobile: this.isTouchDevice,
147
- } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '7218fff9c89f525baf655eea46aec1698a28babd', style: {
238
+ } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '09104351c9f178b8d0dbc0636cf9cc230b2bca53', style: {
148
239
  position: 'absolute',
149
240
  bottom: '56px',
150
241
  left: '12px',
151
- }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '1dc0fa7b02a953474aae9c7822c651d5006e89e5', class: "kritzel-controls" }, this.controls.map(control => {
152
- if (control.type === 'tool') {
153
- // Check if this control has sub-options (split-button)
154
- if (control.subOptions?.length) {
155
- const selectedSubOption = this.getSelectedSubOption(control);
156
- const isActive = this.activeControl?.name === control.name;
157
- const isSubMenuOpen = this.openSubMenuControl?.name === control.name;
158
- return (h("div", { class: {
159
- 'kritzel-control-split': true,
160
- 'selected': isActive,
161
- }, key: control.name, ref: el => {
162
- if (el)
163
- control._anchorRef = el;
164
- } }, h("button", { class: "kritzel-control-main", onClick: () => this.handleControlClick(control), title: selectedSubOption?.label }, h("kritzel-icon", { name: selectedSubOption?.icon || control.icon })), h("button", { class: {
165
- 'kritzel-control-dropdown': true,
166
- 'visible': isActive,
167
- }, onClick: (e) => this.toggleSubMenu(e, control), "aria-label": "Select shape type", "aria-expanded": isSubMenuOpen ? 'true' : 'false', tabIndex: isActive ? 0 : -1 }, h("kritzel-icon", { name: "chevron-down", size: 12 })), h("kritzel-tooltip", { isVisible: isSubMenuOpen, anchorElement: control._anchorRef, onTooltipClosed: () => { this.openSubMenuControl = null; } }, h("div", { class: "kritzel-submenu-content" }, control.subOptions.map(option => (h("button", { class: {
168
- 'kritzel-submenu-item': true,
169
- 'active': option.id === selectedSubOption?.id,
170
- }, key: option.id, onClick: () => this.selectSubOption(control, option) }, h("kritzel-icon", { name: option.icon, size: 20 }), h("span", null, option.label))))))));
171
- }
172
- // Regular tool control (no sub-options)
173
- return (h("button", { class: {
174
- 'kritzel-control': true,
175
- 'selected': this.activeControl?.name === control?.name,
176
- }, key: control.name, onClick: _event => this.handleControlClick?.(control) }, h("kritzel-icon", { name: control.icon })));
177
- }
178
- if (control.type === 'config' && control.name === this.firstConfig?.name && this.activeControl) {
242
+ }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '22f95249dddcf05a75425f96ab5381fe8a4824d4', class: "kritzel-controls" }, h("div", { key: 'fcca99e2a56f401b28205b8806497c46b7f9a94b', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), h("div", { key: '145802334aa83b0190a70dd2a3aca4389cb8b88c', class: "kritzel-tools-scroll", ref: el => {
243
+ this.toolsScrollRef = el;
244
+ // Update indicators when ref is set
245
+ if (el)
246
+ this.updateScrollIndicators();
247
+ }, onScroll: this.handleToolsScroll }, toolControls.map(control => {
248
+ // Check if this control has sub-options (split-button)
249
+ if (control.subOptions?.length) {
250
+ const selectedSubOption = this.getSelectedSubOption(control);
251
+ const isActive = this.activeControl?.name === control.name;
252
+ const isSubMenuOpen = this.openSubMenuControl?.name === control.name;
179
253
  return (h("div", { class: {
180
- 'kritzel-config-container': true,
181
- 'visible': hasConfigUI,
182
- }, key: control.name }, h("kritzel-tooltip", { ref: el => (this.tooltipRef = el), isVisible: this.isTooltipVisible, anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), onTooltipClosed: () => this.handleTooltipClosed() }, h("div", { style: { width: '294px', height: '100%' } }, this.activeControl.name === 'brush' && (h("kritzel-control-brush-config", { tool: this.activeToolAsBrushTool, onToolChange: event => this.handleToolChange?.(event) })), this.activeControl.name === 'text' && (h("kritzel-control-text-config", { tool: this.activeToolAsTextTool, onToolChange: event => this.handleToolChange?.(event) })))), h("div", { tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", onClick: event => this.handleConfigClick?.(event), onKeyDown: event => {
183
- if (event.key === 'Enter') {
184
- this.handleConfigClick?.(event);
185
- }
186
- }, style: {
187
- cursor: 'pointer',
188
- } }, this.activeControl.tool instanceof KritzelBrushTool && (h("div", { class: "color-container" }, h("kritzel-color", { value: this.activeToolAsBrushTool?.color, size: this.activeToolAsBrushTool?.size, style: {
189
- borderRadius: '50%',
190
- border: 'none',
191
- } }))), this.activeControl.tool instanceof KritzelTextTool && (h("div", { class: "font-container" }, h("kritzel-font", { fontFamily: this.activeToolAsTextTool?.fontFamily, size: this.activeToolAsTextTool?.fontSize, color: this.activeToolAsTextTool?.fontColor }))))));
254
+ 'kritzel-control-split': true,
255
+ 'selected': isActive,
256
+ }, key: control.name, ref: el => {
257
+ if (el)
258
+ control._anchorRef = el;
259
+ } }, h("button", { class: "kritzel-control-main", onClick: () => this.handleControlClick(control), title: selectedSubOption?.label }, h("kritzel-icon", { name: selectedSubOption?.icon || control.icon })), h("button", { class: {
260
+ 'kritzel-control-dropdown': true,
261
+ 'visible': isActive,
262
+ }, onClick: (e) => this.toggleSubMenu(e, control), "aria-label": "Select shape type", "aria-expanded": isSubMenuOpen ? 'true' : 'false', tabIndex: isActive ? 0 : -1 }, h("kritzel-icon", { name: "chevron-down", size: 12 })), h("kritzel-tooltip", { isVisible: isSubMenuOpen, anchorElement: control._anchorRef, onTooltipClosed: () => { this.openSubMenuControl = null; } }, h("div", { class: "kritzel-submenu-content" }, control.subOptions.map(option => (h("button", { class: {
263
+ 'kritzel-submenu-item': true,
264
+ 'active': option.id === selectedSubOption?.id,
265
+ }, key: option.id, onClick: () => this.selectSubOption(control, option) }, h("kritzel-icon", { name: option.icon, size: 20 }), h("span", null, option.label))))))));
192
266
  }
193
- }))));
267
+ // Regular tool control (no sub-options)
268
+ return (h("button", { class: {
269
+ 'kritzel-control': true,
270
+ 'selected': this.activeControl?.name === control?.name,
271
+ }, key: control.name, onClick: _event => this.handleControlClick?.(control) }, h("kritzel-icon", { name: control.icon })));
272
+ })), h("div", { key: '43e7811998855f57097b743998363fd6866bfb29', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight } }), configControl && this.activeControl && (h("div", { class: {
273
+ 'kritzel-config-container': true,
274
+ 'visible': hasConfigUI,
275
+ }, key: configControl.name }, h("div", { key: 'c1ff98f16c41d45666915b96931f23e0cf12ed6f', class: "config-gradient-left" }), h("kritzel-tooltip", { key: 'eac68125daa371d751b65ac561e33848cad3bd69', ref: el => (this.tooltipRef = el), isVisible: this.isTooltipVisible, anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), onTooltipClosed: () => this.handleTooltipClosed() }, h("kritzel-tool-config", { key: '83ed025386d9d1b3ec8b52e99930645c4c35b20a', tool: this.activeControl.tool, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), h("div", { key: 'cf18abb832c10826685fb41794a42b8b6a6a7486', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", onClick: event => this.handleConfigClick?.(event), onKeyDown: event => {
276
+ if (event.key === 'Enter') {
277
+ this.handleConfigClick?.(event);
278
+ }
279
+ }, style: {
280
+ cursor: 'pointer',
281
+ } }, this.activeControl.tool instanceof KritzelTextTool && this.displayValues ? (h("div", { class: "font-container" }, h("kritzel-font", { fontFamily: this.displayValues.fontFamily, size: this.displayValues.size, color: this.displayValues.color }))) : this.displayValues && (h("div", { class: "color-container" }, h("kritzel-color", { value: this.displayValues.color, size: this.displayValues.size, style: {
282
+ borderRadius: '50%',
283
+ border: 'none',
284
+ } })))))))));
194
285
  }
195
286
  static get is() { return "kritzel-controls"; }
196
287
  static get encapsulation() { return "shadow"; }
@@ -217,7 +308,8 @@ export class KritzelControls {
217
308
  "KritzelToolbarControl": {
218
309
  "location": "import",
219
310
  "path": "../../../interfaces/toolbar-control.interface",
220
- "id": "src/interfaces/toolbar-control.interface.ts::KritzelToolbarControl"
311
+ "id": "src/interfaces/toolbar-control.interface.ts::KritzelToolbarControl",
312
+ "referenceLocation": "KritzelToolbarControl"
221
313
  }
222
314
  }
223
315
  },
@@ -241,7 +333,8 @@ export class KritzelControls {
241
333
  "KritzelToolbarControl": {
242
334
  "location": "import",
243
335
  "path": "../../../interfaces/toolbar-control.interface",
244
- "id": "src/interfaces/toolbar-control.interface.ts::KritzelToolbarControl"
336
+ "id": "src/interfaces/toolbar-control.interface.ts::KritzelToolbarControl",
337
+ "referenceLocation": "KritzelToolbarControl"
245
338
  }
246
339
  }
247
340
  },
@@ -285,7 +378,8 @@ export class KritzelControls {
285
378
  "KritzelUndoState": {
286
379
  "location": "import",
287
380
  "path": "../../../interfaces/undo-state.interface",
288
- "id": "src/interfaces/undo-state.interface.ts::KritzelUndoState"
381
+ "id": "src/interfaces/undo-state.interface.ts::KritzelUndoState",
382
+ "referenceLocation": "KritzelUndoState"
289
383
  }
290
384
  }
291
385
  },
@@ -307,7 +401,10 @@ export class KritzelControls {
307
401
  "isTooltipVisible": {},
308
402
  "isTouchDevice": {},
309
403
  "selectedSubOptions": {},
310
- "openSubMenuControl": {}
404
+ "openSubMenuControl": {},
405
+ "canScrollLeft": {},
406
+ "canScrollRight": {},
407
+ "displayValues": {}
311
408
  };
312
409
  }
313
410
  static get events() {
@@ -369,6 +466,12 @@ export class KritzelControls {
369
466
  "target": "document",
370
467
  "capture": false,
371
468
  "passive": false
469
+ }, {
470
+ "name": "objectsSelectionChange",
471
+ "method": "handleSelectionChange",
472
+ "target": "document",
473
+ "capture": false,
474
+ "passive": false
372
475
  }];
373
476
  }
374
477
  }
@@ -0,0 +1,38 @@
1
+ .expand-toggle {
2
+ background: none;
3
+ border: none;
4
+ cursor: pointer;
5
+ padding: 0;
6
+ margin: 0;
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ width: 32px;
11
+ height: 32px;
12
+ color: var(--kritzel-icon-color, currentColor);
13
+ transition: transform 0.2s ease;
14
+ }
15
+
16
+ .expand-toggle:hover {
17
+ opacity: 0.7;
18
+ }
19
+
20
+ .expand-toggle:focus {
21
+ outline: none;
22
+ }
23
+
24
+ .expand-toggle:focus-visible {
25
+ outline: 2px solid var(--kritzel-focus-color, #007acc);
26
+ outline-offset: 2px;
27
+ }
28
+
29
+ .expand-toggle:active {
30
+ transform: scale(0.95);
31
+ }
32
+
33
+ .divider {
34
+ height: 1px;
35
+ background-color: var(--kritzel-divider-color, #e0e0e0);
36
+ margin: 4px 0;
37
+ width: 100%;
38
+ }
@@ -0,0 +1,321 @@
1
+ import { Host, h } from "@stencil/core";
2
+ import { KritzelToolConfigHelper } from "../../../helpers/tool-config.helper";
3
+ import { KritzelTextTool } from "../../../classes/tools/text-tool.class";
4
+ import { KritzelSelectionTool } from "../../../classes/tools/selection-tool.class";
5
+ import { KritzelColorHelper } from "../../../helpers/color.helper";
6
+ export class KritzelToolConfig {
7
+ tool;
8
+ handleToolChange(newTool, oldTool) {
9
+ const newConfig = KritzelToolConfigHelper.getToolConfig(newTool);
10
+ // Maintain settings when switching between shape tools
11
+ if (oldTool && newTool && newConfig?.type === 'shape') {
12
+ const oldConfig = KritzelToolConfigHelper.getToolConfig(oldTool);
13
+ if (oldConfig?.type === 'shape') {
14
+ // Copy properties that should persist
15
+ const propsToCopy = [
16
+ newConfig.colorProperty, // strokeColor
17
+ newConfig.sizeProperty, // strokeWidth
18
+ newConfig.opacityProperty, // opacity
19
+ 'fillColor' // shape specific
20
+ ];
21
+ propsToCopy.forEach(prop => {
22
+ if (prop && oldTool[prop] !== undefined) {
23
+ newTool[prop] = oldTool[prop];
24
+ }
25
+ });
26
+ }
27
+ }
28
+ this.config = newConfig;
29
+ if (this.config) {
30
+ this.updatePalette();
31
+ this.currentOpacity = newTool[this.config.opacityProperty] ?? 1;
32
+ // Emit the values since they might have been updated from the old tool
33
+ this.emitDisplayValues();
34
+ }
35
+ }
36
+ isExpanded = false;
37
+ toolChange;
38
+ displayValuesChange;
39
+ config;
40
+ palette = [];
41
+ currentOpacity = 1;
42
+ updateTrigger = 0;
43
+ handleSelectionChange() {
44
+ if (this.tool instanceof KritzelSelectionTool) {
45
+ this.config = KritzelToolConfigHelper.getToolConfig(this.tool);
46
+ if (this.config) {
47
+ this.updatePalette();
48
+ this.currentOpacity = this.tool[this.config.opacityProperty] ?? 1;
49
+ this.emitDisplayValues();
50
+ }
51
+ }
52
+ }
53
+ componentWillLoad() {
54
+ this.config = KritzelToolConfigHelper.getToolConfig(this.tool);
55
+ if (this.config) {
56
+ this.updatePalette();
57
+ this.currentOpacity = this.tool[this.config.opacityProperty] ?? 1;
58
+ this.emitDisplayValues();
59
+ }
60
+ }
61
+ emitDisplayValues() {
62
+ if (!this.config)
63
+ return;
64
+ const color = this.tool[this.config.colorProperty];
65
+ const opacity = this.currentOpacity;
66
+ const size = this.tool[this.config.sizeProperty];
67
+ const displayValues = {
68
+ color: KritzelColorHelper.applyOpacity(color, opacity),
69
+ size,
70
+ };
71
+ if (this.tool instanceof KritzelTextTool) {
72
+ displayValues.fontFamily = this.tool.fontFamily;
73
+ }
74
+ this.displayValuesChange.emit(displayValues);
75
+ }
76
+ updatePalette() {
77
+ if (!this.config)
78
+ return;
79
+ if (this.config.paletteSource === 'palettes') {
80
+ // Brush tool has palettes[type]
81
+ const brushTool = this.tool;
82
+ this.palette = brushTool.palettes[brushTool.type] || [];
83
+ }
84
+ else if (this.config.paletteSource === 'palette') {
85
+ // Line, Shape, Text tools have palette
86
+ this.palette = this.tool.palette || [];
87
+ }
88
+ }
89
+ handleToggleExpand = () => {
90
+ this.isExpanded = !this.isExpanded;
91
+ };
92
+ handleColorChange = (event) => {
93
+ this.tool[this.config.colorProperty] = event.detail;
94
+ // Special handling for shape fill: when color (stroke) changes, update fill color if it's currently filled
95
+ if (this.config.type === 'shape' || this.config.type === 'selection') {
96
+ const tool = this.tool;
97
+ if (tool.fillColor !== 'transparent') {
98
+ tool.fillColor = event.detail;
99
+ }
100
+ }
101
+ this.emitDisplayValues();
102
+ this.toolChange.emit(this.tool);
103
+ this.updateTrigger++;
104
+ };
105
+ handleSizeChange = (event) => {
106
+ this.tool[this.config.sizeProperty] = event.detail;
107
+ this.emitDisplayValues();
108
+ this.toolChange.emit(this.tool);
109
+ this.updateTrigger++;
110
+ };
111
+ handleOpacityChange = (event) => {
112
+ this.tool[this.config.opacityProperty] = event.detail;
113
+ this.currentOpacity = event.detail;
114
+ this.emitDisplayValues();
115
+ this.toolChange.emit(this.tool);
116
+ };
117
+ handlePropertyChange = (propertyName, value) => {
118
+ // Special handling for shape fill
119
+ if ((this.config.type === 'shape' || this.config.type === 'selection') && propertyName === 'fillColor') {
120
+ const newFillColor = value === 'filled' ? this.tool[this.config.colorProperty] : 'transparent';
121
+ this.tool.fillColor = newFillColor;
122
+ // When switching to fill mode, also update stroke color to match
123
+ if (value === 'filled') {
124
+ this.tool[this.config.colorProperty] = newFillColor;
125
+ }
126
+ }
127
+ // Special handling for brush type change
128
+ else if (this.config.type === 'brush' && propertyName === 'type') {
129
+ const brushTool = this.tool;
130
+ brushTool.type = value;
131
+ this.palette = brushTool.palettes[value];
132
+ brushTool.color = this.palette[0];
133
+ this.emitDisplayValues();
134
+ }
135
+ else {
136
+ this.tool[propertyName] = value;
137
+ // Emit display values for font family changes
138
+ if (propertyName === 'fontFamily') {
139
+ this.emitDisplayValues();
140
+ }
141
+ }
142
+ this.toolChange.emit(this.tool);
143
+ this.updateTrigger++;
144
+ };
145
+ getShapeFillValue() {
146
+ const fillColor = this.tool.fillColor;
147
+ return fillColor === 'transparent' ? 'transparent' : 'filled';
148
+ }
149
+ renderControl(control) {
150
+ const value = this.tool[control.propertyName];
151
+ switch (control.type) {
152
+ case 'stroke-size':
153
+ return (h("kritzel-stroke-size", { key: control.type, selectedSize: value, onSizeChange: this.handleSizeChange }));
154
+ case 'font-size':
155
+ return (h("kritzel-font-size", { key: control.type, selectedSize: value, fontFamily: this.tool.fontFamily, onSizeChange: this.handleSizeChange }));
156
+ case 'line-endings':
157
+ return (h("kritzel-line-endings", { key: control.type, value: value, strokeColor: this.tool[this.config.colorProperty], onValueChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
158
+ case 'shape-fill':
159
+ return (h("kritzel-shape-fill", { key: control.type, value: this.getShapeFillValue(), onValueChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
160
+ case 'font-family':
161
+ return (h("kritzel-font-family", { key: control.type, selectedFontFamily: value, onFontFamilyChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
162
+ default:
163
+ return null;
164
+ }
165
+ }
166
+ render() {
167
+ if (!this.config)
168
+ return null;
169
+ const shouldShowExpandButton = this.palette.length > 6 || this.config.type === 'text';
170
+ // Separate size control from other controls
171
+ const sizeControl = this.config.controls.find(c => c.type === 'stroke-size' || c.type === 'font-size');
172
+ const otherControls = this.config.controls.filter(c => c.type !== 'stroke-size' && c.type !== 'font-size');
173
+ return (h(Host, null, h("div", { style: {
174
+ display: 'flex',
175
+ flexDirection: 'row',
176
+ gap: '8px',
177
+ width: '100%',
178
+ } }, h("div", { style: {
179
+ display: 'flex',
180
+ flexDirection: 'column',
181
+ gap: '12px',
182
+ flex: '1',
183
+ } }, h("kritzel-color-palette", { colors: this.palette, selectedColor: this.tool[this.config.colorProperty], isExpanded: this.isExpanded, isOpaque: true, opacity: this.currentOpacity, onColorChange: this.handleColorChange }), sizeControl && this.renderControl(sizeControl), h("kritzel-opacity-slider", { value: this.tool[this.config.opacityProperty], previewColor: this.tool[this.config.colorProperty], onValueChange: this.handleOpacityChange }), otherControls.map((control) => [
184
+ h("div", { class: "divider" }),
185
+ this.renderControl(control),
186
+ ])), shouldShowExpandButton && (h("div", { style: {
187
+ display: 'flex',
188
+ alignItems: 'flex-start',
189
+ } }, h("button", { class: "expand-toggle", onClick: this.handleToggleExpand, title: this.isExpanded ? 'Collapse' : 'Expand' }, h("kritzel-icon", { name: this.isExpanded ? 'chevron-up' : 'chevron-down' })))))));
190
+ }
191
+ static get is() { return "kritzel-tool-config"; }
192
+ static get encapsulation() { return "shadow"; }
193
+ static get originalStyleUrls() {
194
+ return {
195
+ "$": ["kritzel-tool-config.css"]
196
+ };
197
+ }
198
+ static get styleUrls() {
199
+ return {
200
+ "$": ["kritzel-tool-config.css"]
201
+ };
202
+ }
203
+ static get properties() {
204
+ return {
205
+ "tool": {
206
+ "type": "unknown",
207
+ "mutable": true,
208
+ "complexType": {
209
+ "original": "ConfigurableTool",
210
+ "resolved": "KritzelBrushTool | KritzelLineTool | KritzelSelectionTool | KritzelShapeTool | KritzelTextTool",
211
+ "references": {
212
+ "ConfigurableTool": {
213
+ "location": "import",
214
+ "path": "../../../interfaces/tool-config.interface",
215
+ "id": "src/interfaces/tool-config.interface.ts::ConfigurableTool",
216
+ "referenceLocation": "ConfigurableTool"
217
+ }
218
+ }
219
+ },
220
+ "required": false,
221
+ "optional": false,
222
+ "docs": {
223
+ "tags": [],
224
+ "text": ""
225
+ },
226
+ "getter": false,
227
+ "setter": false
228
+ },
229
+ "isExpanded": {
230
+ "type": "boolean",
231
+ "mutable": true,
232
+ "complexType": {
233
+ "original": "boolean",
234
+ "resolved": "boolean",
235
+ "references": {}
236
+ },
237
+ "required": false,
238
+ "optional": false,
239
+ "docs": {
240
+ "tags": [],
241
+ "text": ""
242
+ },
243
+ "getter": false,
244
+ "setter": false,
245
+ "reflect": false,
246
+ "attribute": "is-expanded",
247
+ "defaultValue": "false"
248
+ }
249
+ };
250
+ }
251
+ static get states() {
252
+ return {
253
+ "config": {},
254
+ "palette": {},
255
+ "currentOpacity": {},
256
+ "updateTrigger": {}
257
+ };
258
+ }
259
+ static get events() {
260
+ return [{
261
+ "method": "toolChange",
262
+ "name": "toolChange",
263
+ "bubbles": true,
264
+ "cancelable": true,
265
+ "composed": true,
266
+ "docs": {
267
+ "tags": [],
268
+ "text": ""
269
+ },
270
+ "complexType": {
271
+ "original": "ConfigurableTool",
272
+ "resolved": "KritzelBrushTool | KritzelLineTool | KritzelSelectionTool | KritzelShapeTool | KritzelTextTool",
273
+ "references": {
274
+ "ConfigurableTool": {
275
+ "location": "import",
276
+ "path": "../../../interfaces/tool-config.interface",
277
+ "id": "src/interfaces/tool-config.interface.ts::ConfigurableTool",
278
+ "referenceLocation": "ConfigurableTool"
279
+ }
280
+ }
281
+ }
282
+ }, {
283
+ "method": "displayValuesChange",
284
+ "name": "displayValuesChange",
285
+ "bubbles": true,
286
+ "cancelable": true,
287
+ "composed": true,
288
+ "docs": {
289
+ "tags": [],
290
+ "text": ""
291
+ },
292
+ "complexType": {
293
+ "original": "ToolDisplayValues",
294
+ "resolved": "ToolDisplayValues",
295
+ "references": {
296
+ "ToolDisplayValues": {
297
+ "location": "import",
298
+ "path": "../../../interfaces/tool-config.interface",
299
+ "id": "src/interfaces/tool-config.interface.ts::ToolDisplayValues",
300
+ "referenceLocation": "ToolDisplayValues"
301
+ }
302
+ }
303
+ }
304
+ }];
305
+ }
306
+ static get watchers() {
307
+ return [{
308
+ "propName": "tool",
309
+ "methodName": "handleToolChange"
310
+ }];
311
+ }
312
+ static get listeners() {
313
+ return [{
314
+ "name": "objectsSelectionChange",
315
+ "method": "handleSelectionChange",
316
+ "target": "document",
317
+ "capture": false,
318
+ "passive": false
319
+ }];
320
+ }
321
+ }