@pooder/kit 0.0.2 → 2.0.0

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/src/ruler.ts CHANGED
@@ -1,238 +1,255 @@
1
- import { Command, Editor, EditorState, Extension, OptionSchema, PooderLayer, Rect, Line, Text } from '@pooder/core';
2
-
3
- export interface RulerToolOptions {
4
- unit: 'px' | 'mm' | 'cm' | 'in';
5
- thickness: number;
6
- backgroundColor: string;
7
- textColor: string;
8
- lineColor: string;
9
- fontSize: number;
10
- }
11
-
12
- export class RulerTool implements Extension<RulerToolOptions> {
13
- public name = 'RulerTool';
14
- public options: RulerToolOptions = {
15
- unit: 'px',
16
- thickness: 20,
17
- backgroundColor: '#f0f0f0',
18
- textColor: '#333333',
19
- lineColor: '#999999',
20
- fontSize: 10
21
- };
22
-
23
- public schema: Record<keyof RulerToolOptions, OptionSchema> = {
24
- unit: {
25
- type: 'select',
26
- options: ['px', 'mm', 'cm', 'in'],
27
- label: 'Unit'
28
- },
29
- thickness: { type: 'number', min: 10, max: 100, label: 'Thickness' },
30
- backgroundColor: { type: 'color', label: 'Background Color' },
31
- textColor: { type: 'color', label: 'Text Color' },
32
- lineColor: { type: 'color', label: 'Line Color' },
33
- fontSize: { type: 'number', min: 8, max: 24, label: 'Font Size' }
34
- };
35
-
36
- onMount(editor: Editor) {
37
- this.createLayer(editor);
38
- }
39
-
40
- onUnmount(editor: Editor) {
41
- this.destroyLayer(editor);
42
- }
43
-
44
- onUpdate(editor: Editor, state: EditorState) {
45
- }
46
-
47
- onDestroy(editor: Editor) {
48
- this.destroyLayer(editor);
49
- }
50
-
51
- private getLayer(editor: Editor) {
52
- return editor.canvas.getObjects().find((obj: any) => obj.data?.id === 'ruler-overlay') as PooderLayer | undefined;
53
- }
54
-
55
- private createLayer(editor: Editor) {
56
- let layer = this.getLayer(editor);
57
-
58
- if (!layer) {
59
- const width = editor.canvas.width || 800;
60
- const height = editor.canvas.height || 600;
61
-
62
- layer = new PooderLayer([], {
63
- width,
64
- height,
65
- selectable: false,
66
- evented: false,
67
- data: { id: 'ruler-overlay' }
68
- } as any);
69
-
70
- editor.canvas.add(layer);
71
- }
72
-
73
- editor.canvas.bringObjectToFront(layer);
74
- this.updateRuler(editor);
75
- }
76
-
77
- private destroyLayer(editor: Editor) {
78
- const layer = this.getLayer(editor);
79
- if (layer) {
80
- editor.canvas.remove(layer);
81
- }
82
- }
83
-
84
- private updateRuler(editor: Editor) {
85
- const layer = this.getLayer(editor);
86
- if (!layer) return;
87
-
88
- layer.remove(...layer.getObjects());
89
-
90
- const { thickness, backgroundColor, lineColor, textColor, fontSize } = this.options;
91
- const width = editor.canvas.width || 800;
92
- const height = editor.canvas.height || 600;
93
-
94
- // Backgrounds
95
- const topBg = new Rect({
96
- left: 0,
97
- top: 0,
98
- width: width,
99
- height: thickness,
100
- fill: backgroundColor,
101
- selectable: false,
102
- evented: false
103
- });
104
-
105
- const leftBg = new Rect({
106
- left: 0,
107
- top: 0,
108
- width: thickness,
109
- height: height,
110
- fill: backgroundColor,
111
- selectable: false,
112
- evented: false
113
- });
114
-
115
- const cornerBg = new Rect({
116
- left: 0,
117
- top: 0,
118
- width: thickness,
119
- height: thickness,
120
- fill: backgroundColor,
121
- stroke: lineColor,
122
- strokeWidth: 1,
123
- selectable: false,
124
- evented: false
125
- });
126
-
127
- layer.add(topBg, leftBg, cornerBg);
128
-
129
- // Drawing Constants (Pixel based for now)
130
- const step = 100; // Major tick
131
- const subStep = 10; // Minor tick
132
- const midStep = 50; // Medium tick
133
-
134
- // Top Ruler
135
- for (let x = 0; x <= width; x += subStep) {
136
- if (x < thickness) continue; // Skip corner
137
-
138
- let len = thickness * 0.25;
139
- if (x % step === 0) len = thickness * 0.8;
140
- else if (x % midStep === 0) len = thickness * 0.5;
141
-
142
- const line = new Line([x, thickness - len, x, thickness], {
143
- stroke: lineColor,
144
- strokeWidth: 1,
145
- selectable: false,
146
- evented: false
147
- });
148
- layer.add(line);
149
-
150
- if (x % step === 0) {
151
- const text = new Text(x.toString(), {
152
- left: x + 2,
153
- top: 2,
154
- fontSize: fontSize,
155
- fill: textColor,
156
- fontFamily: 'Arial',
157
- selectable: false,
158
- evented: false
159
- });
160
- layer.add(text);
161
- }
162
- }
163
-
164
- // Left Ruler
165
- for (let y = 0; y <= height; y += subStep) {
166
- if (y < thickness) continue; // Skip corner
167
-
168
- let len = thickness * 0.25;
169
- if (y % step === 0) len = thickness * 0.8;
170
- else if (y % midStep === 0) len = thickness * 0.5;
171
-
172
- const line = new Line([thickness - len, y, thickness, y], {
173
- stroke: lineColor,
174
- strokeWidth: 1,
175
- selectable: false,
176
- evented: false
177
- });
178
- layer.add(line);
179
-
180
- if (y % step === 0) {
181
- const text = new Text(y.toString(), {
182
- angle: -90,
183
- left: thickness / 2 - fontSize / 3, // approximate centering
184
- top: y + fontSize,
185
- fontSize: fontSize,
186
- fill: textColor,
187
- fontFamily: 'Arial',
188
- originX: 'center',
189
- originY: 'center',
190
- selectable: false,
191
- evented: false
192
- });
193
-
194
- layer.add(text);
195
- }
196
- }
197
-
198
- // Always bring ruler to front
199
- editor.canvas.bringObjectToFront(layer);
200
- editor.canvas.requestRenderAll();
201
- }
202
-
203
- commands: Record<string, Command> = {
204
- setUnit: {
205
- execute: (editor: Editor, unit: 'px' | 'mm' | 'cm' | 'in') => {
206
- if (this.options.unit === unit) return true;
207
- this.options.unit = unit;
208
- this.updateRuler(editor);
209
- return true;
210
- },
211
- schema: {
212
- unit: {
213
- type: 'string',
214
- label: 'Unit',
215
- options: ['px', 'mm', 'cm', 'in'],
216
- required: true
217
- }
218
- }
219
- },
220
- setTheme: {
221
- execute: (editor: Editor, theme: Partial<RulerToolOptions>) => {
222
- const newOptions = { ...this.options, ...theme };
223
- if (JSON.stringify(newOptions) === JSON.stringify(this.options)) return true;
224
-
225
- this.options = newOptions;
226
- this.updateRuler(editor);
227
- return true;
228
- },
229
- schema: {
230
- theme: {
231
- type: 'object',
232
- label: 'Theme',
233
- required: true
234
- }
235
- }
236
- }
237
- };
238
- }
1
+ import {
2
+ Command,
3
+ Editor,
4
+ EditorState,
5
+ Extension,
6
+ OptionSchema,
7
+ PooderLayer,
8
+ Rect,
9
+ Line,
10
+ Text,
11
+ } from "@pooder/core";
12
+
13
+ export interface RulerToolOptions {
14
+ unit: "px" | "mm" | "cm" | "in";
15
+ thickness: number;
16
+ backgroundColor: string;
17
+ textColor: string;
18
+ lineColor: string;
19
+ fontSize: number;
20
+ }
21
+
22
+ export class RulerTool implements Extension<RulerToolOptions> {
23
+ public name = "RulerTool";
24
+ public options: RulerToolOptions = {
25
+ unit: "px",
26
+ thickness: 20,
27
+ backgroundColor: "#f0f0f0",
28
+ textColor: "#333333",
29
+ lineColor: "#999999",
30
+ fontSize: 10,
31
+ };
32
+
33
+ public schema: Record<keyof RulerToolOptions, OptionSchema> = {
34
+ unit: {
35
+ type: "select",
36
+ options: ["px", "mm", "cm", "in"],
37
+ label: "Unit",
38
+ },
39
+ thickness: { type: "number", min: 10, max: 100, label: "Thickness" },
40
+ backgroundColor: { type: "color", label: "Background Color" },
41
+ textColor: { type: "color", label: "Text Color" },
42
+ lineColor: { type: "color", label: "Line Color" },
43
+ fontSize: { type: "number", min: 8, max: 24, label: "Font Size" },
44
+ };
45
+
46
+ onMount(editor: Editor) {
47
+ this.createLayer(editor);
48
+ this.updateRuler(editor);
49
+ }
50
+
51
+ onUnmount(editor: Editor) {
52
+ this.destroyLayer(editor);
53
+ }
54
+
55
+ onUpdate(editor: Editor, state: EditorState) {
56
+ this.updateRuler(editor);
57
+ }
58
+
59
+ onDestroy(editor: Editor) {
60
+ this.destroyLayer(editor);
61
+ }
62
+
63
+ private getLayer(editor: Editor) {
64
+ return editor.canvas
65
+ .getObjects()
66
+ .find((obj: any) => obj.data?.id === "ruler-overlay") as
67
+ | PooderLayer
68
+ | undefined;
69
+ }
70
+
71
+ private createLayer(editor: Editor) {
72
+ let layer = this.getLayer(editor);
73
+
74
+ if (!layer) {
75
+ const width = editor.canvas.width || 800;
76
+ const height = editor.canvas.height || 600;
77
+
78
+ layer = new PooderLayer([], {
79
+ width,
80
+ height,
81
+ selectable: false,
82
+ evented: false,
83
+ data: { id: "ruler-overlay" },
84
+ } as any);
85
+
86
+ editor.canvas.add(layer);
87
+ }
88
+
89
+ editor.canvas.bringObjectToFront(layer);
90
+ }
91
+
92
+ private destroyLayer(editor: Editor) {
93
+ const layer = this.getLayer(editor);
94
+ if (layer) {
95
+ editor.canvas.remove(layer);
96
+ }
97
+ }
98
+
99
+ private updateRuler(editor: Editor) {
100
+ const layer = this.getLayer(editor);
101
+ if (!layer) return;
102
+
103
+ layer.remove(...layer.getObjects());
104
+
105
+ const { thickness, backgroundColor, lineColor, textColor, fontSize } =
106
+ this.options;
107
+ const width = editor.canvas.width || 800;
108
+ const height = editor.canvas.height || 600;
109
+
110
+ // Backgrounds
111
+ const topBg = new Rect({
112
+ left: 0,
113
+ top: 0,
114
+ width: width,
115
+ height: thickness,
116
+ fill: backgroundColor,
117
+ selectable: false,
118
+ evented: false,
119
+ });
120
+
121
+ const leftBg = new Rect({
122
+ left: 0,
123
+ top: 0,
124
+ width: thickness,
125
+ height: height,
126
+ fill: backgroundColor,
127
+ selectable: false,
128
+ evented: false,
129
+ });
130
+
131
+ const cornerBg = new Rect({
132
+ left: 0,
133
+ top: 0,
134
+ width: thickness,
135
+ height: thickness,
136
+ fill: backgroundColor,
137
+ stroke: lineColor,
138
+ strokeWidth: 1,
139
+ selectable: false,
140
+ evented: false,
141
+ });
142
+
143
+ layer.add(topBg, leftBg, cornerBg);
144
+
145
+ // Drawing Constants (Pixel based for now)
146
+ const step = 100; // Major tick
147
+ const subStep = 10; // Minor tick
148
+ const midStep = 50; // Medium tick
149
+
150
+ // Top Ruler
151
+ for (let x = 0; x <= width; x += subStep) {
152
+ if (x < thickness) continue; // Skip corner
153
+
154
+ let len = thickness * 0.25;
155
+ if (x % step === 0) len = thickness * 0.8;
156
+ else if (x % midStep === 0) len = thickness * 0.5;
157
+
158
+ const line = new Line([x, thickness - len, x, thickness], {
159
+ stroke: lineColor,
160
+ strokeWidth: 1,
161
+ selectable: false,
162
+ evented: false,
163
+ });
164
+ layer.add(line);
165
+
166
+ if (x % step === 0) {
167
+ const text = new Text(x.toString(), {
168
+ left: x + 2,
169
+ top: 2,
170
+ fontSize: fontSize,
171
+ fill: textColor,
172
+ fontFamily: "Arial",
173
+ selectable: false,
174
+ evented: false,
175
+ });
176
+ layer.add(text);
177
+ }
178
+ }
179
+
180
+ // Left Ruler
181
+ for (let y = 0; y <= height; y += subStep) {
182
+ if (y < thickness) continue; // Skip corner
183
+
184
+ let len = thickness * 0.25;
185
+ if (y % step === 0) len = thickness * 0.8;
186
+ else if (y % midStep === 0) len = thickness * 0.5;
187
+
188
+ const line = new Line([thickness - len, y, thickness, y], {
189
+ stroke: lineColor,
190
+ strokeWidth: 1,
191
+ selectable: false,
192
+ evented: false,
193
+ });
194
+ layer.add(line);
195
+
196
+ if (y % step === 0) {
197
+ const text = new Text(y.toString(), {
198
+ angle: -90,
199
+ left: thickness / 2 - fontSize / 3, // approximate centering
200
+ top: y + fontSize,
201
+ fontSize: fontSize,
202
+ fill: textColor,
203
+ fontFamily: "Arial",
204
+ originX: "center",
205
+ originY: "center",
206
+ selectable: false,
207
+ evented: false,
208
+ });
209
+
210
+ layer.add(text);
211
+ }
212
+ }
213
+
214
+ // Always bring ruler to front
215
+ editor.canvas.bringObjectToFront(layer);
216
+ editor.canvas.requestRenderAll();
217
+ }
218
+
219
+ commands: Record<string, Command> = {
220
+ setUnit: {
221
+ execute: (editor: Editor, unit: "px" | "mm" | "cm" | "in") => {
222
+ if (this.options.unit === unit) return true;
223
+ this.options.unit = unit;
224
+ this.updateRuler(editor);
225
+ return true;
226
+ },
227
+ schema: {
228
+ unit: {
229
+ type: "string",
230
+ label: "Unit",
231
+ options: ["px", "mm", "cm", "in"],
232
+ required: true,
233
+ },
234
+ },
235
+ },
236
+ setTheme: {
237
+ execute: (editor: Editor, theme: Partial<RulerToolOptions>) => {
238
+ const newOptions = { ...this.options, ...theme };
239
+ if (JSON.stringify(newOptions) === JSON.stringify(this.options))
240
+ return true;
241
+
242
+ this.options = newOptions;
243
+ this.updateRuler(editor);
244
+ return true;
245
+ },
246
+ schema: {
247
+ theme: {
248
+ type: "object",
249
+ label: "Theme",
250
+ required: true,
251
+ },
252
+ },
253
+ },
254
+ };
255
+ }