@pictogrammers/components 0.5.15 → 0.5.18
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/package.json +1 -1
- package/pg/autocompleteItem/__examples__/basic/basic.html +3 -0
- package/pg/autocompleteItem/autocompleteItem.css +55 -0
- package/pg/autocompleteItem/autocompleteItem.html +1 -0
- package/pg/autocompleteItem/autocompleteItem.ts +65 -0
- package/pg/button/button.css +5 -0
- package/pg/buttonMenu/buttonMenu.ts +1 -1
- package/pg/inputCombobox/__examples__/basic/basic.ts +33 -0
- package/pg/inputCombobox/inputCombobox.ts +158 -0
- package/pg/inputCombobox/inputCombobox.ts.tmp.44252.751480cb81cc +158 -0
- package/pg/inputCombobox/inputCombobox.ts.tmp.44252.dcea847c40a8 +156 -0
- package/pg/inputPill/inputPill.html +6 -0
- package/pg/inputPill/inputPill.html.tmp.26852.84ac63f4c46f +6 -0
- package/pg/inputPill/inputPill.ts +41 -0
- package/pg/inputPills/__examples__/basic/basic.html +9 -0
- package/pg/inputPills/__examples__/basic/basic.html.tmp.26852.4771e4cad5b7 +9 -0
- package/pg/inputPills/__examples__/basic/basic.ts.tmp.26852.d8fe41df18bd +0 -0
- package/pg/inputPixelEditor/README.md +104 -69
- package/pg/inputPixelEditor/README.md.tmp.42596.658de5f0a6f5 +0 -0
- package/pg/inputPixelEditor/__examples__/basic/basic.ts +6 -5
- package/pg/inputPixelEditor/inputPixelEditor.ts +96 -24
- package/pg/inputPixelEditor/inputPixelEditor.ts.tmp.38516.96529dd82670 +1703 -0
- package/pg/inputPixelEditor/inputPixelEditor.ts.tmp.42596.48ecc3ad9c72 +1724 -0
- package/pg/inputPixelEditor/utils/bitmapToMask.ts +113 -79
- package/pg/inputPixelEditor/utils/getLassoPixels.ts +51 -0
- package/pg/inputSelect/inputSelect.ts +5 -1
- package/pg/inputText/README.md +6 -6
- package/pg/inputText/__examples__/basic/basic.ts +1 -1
- package/pg/inputText/inputText.css +2 -2
- package/pg/inputText copy/README.md +38 -0
- package/pg/inputText copy/inputText.css +41 -0
- package/pg/inputText copy/inputText.html +1 -0
- package/pg/inputText copy/inputText.spec.ts +59 -0
- package/pg/inputText copy/inputText.ts +67 -0
- package/pg/inputTextarea/README.md +39 -0
- package/pg/inputTextarea/__examples__/basic/basic.html +9 -0
- package/pg/inputTextarea/__examples__/basic/basic.ts +30 -0
- package/pg/inputTextarea/inputTextarea.ts +121 -0
- package/pg/inputTextarea copy/README.md +39 -0
- package/pg/inputTextarea copy/inputTextarea.css +41 -0
- package/pg/inputTextarea copy/inputTextarea.html +1 -0
- package/pg/inputTextarea copy/inputTextarea.spec.ts +59 -0
- package/pg/inputTextarea copy/inputTextarea.ts +79 -0
- package/pg/inputTextareaAutocomplete/README.md +70 -0
- package/pg/inputTextareaAutocomplete/__examples__/basic/basic.ts +67 -0
- package/pg/inputTextareaAutocomplete/inputTextareaAutocomplete.ts +267 -0
- package/pg/node/README.md +35 -0
- package/pg/node/__examples__/basic/basic.ts +34 -0
- package/pg/node/index.ts +3 -0
- package/pg/node/node.css +53 -0
- package/pg/node/node.html +5 -0
- package/pg/node/node.ts +270 -0
- package/pg/nodeEditorLink/nodeEditorLink.css +69 -0
- package/pg/nodeEditorLink/nodeEditorLink.css.tmp.1408.09e961fd0dcc +69 -0
- package/pg/nodeEditorLink/nodeEditorLink.html +9 -0
- package/pg/nodeEditorNumber/nodeEditorNumber.css +59 -0
- package/pg/nodeEditorNumber/nodeEditorNumber.ts +57 -0
- package/pg/nodeEditorRange/nodeEditorRange.css +23 -0
- package/pg/nodeEditorRange/nodeEditorRange.ts +69 -0
- package/pg/nodeEditorText/__examples__/basic/basic.html +9 -0
- package/pg/nodeEditorText/__examples__/basic/basic.ts +30 -0
- package/pg/nodeEditorText/nodeEditorText.css +17 -0
- package/pg/nodeEditorText/nodeEditorText.html +4 -0
- package/pg/nodeEditorText/nodeEditorText.ts +57 -0
- package/pg/nodeEditorText copy/README.md +35 -0
- package/pg/nodeEditorText copy/nodeEditorText.css +49 -0
- package/pg/nodeEditorText copy/nodeEditorText.html +4 -0
- package/pg/nodeEditorText copy/nodeEditorText.ts +50 -0
- package/pg/nodeEditorTextArray/nodeEditorTextArray.css +68 -0
- package/pg/nodeEditorTextArray/nodeEditorTextArray.html +9 -0
- package/pg/nodeEditorTextArray/nodeEditorTextArray.ts +162 -0
- package/pg/nodeEntry/nodeEntry.css +61 -0
- package/pg/nodeEntry/nodeEntry.ts +164 -0
- package/pg/nodeEntry/nodeEntry.ts.tmp.28724.dda67762bca5 +164 -0
- package/pg/nodeInputText/nodeInputText.css +75 -0
- package/pg/nodeInputText/nodeInputText.html +4 -0
- package/pg/nodeInputText/nodeInputText.ts +105 -0
- package/pg/nodeOutput/nodeOutput.css +32 -0
- package/pg/nodeResize/README.md +3 -0
- package/pg/nodeResize/THINKING.md +81 -0
- package/pg/nodeResize/THINKING.md.tmp.24812.99a70ba81062 +81 -0
- package/pg/nodeResize/basic/basic.ts +17 -0
- package/pg/nodeResize/dragUtil.ts +105 -0
- package/pg/nodeResize/dragUtil.ts.tmp.34372.787464c73309 +75 -0
- package/pg/nodeResize/nodeResize.css +187 -0
- package/pg/nodeResize/nodeResize.html +9 -0
- package/pg/nodeResize/nodeResize.ts +156 -0
- package/pg/nodes/README.md +177 -0
- package/pg/nodes/__examples__/basic/basic.html +33 -0
- package/pg/nodes/__examples__/basic/basic.ts +716 -0
- package/pg/nodes/__examples__/basic/exampleNodes.ts +129 -0
- package/pg/nodes/__examples__/basic/exampleScripts.ts +129 -0
- package/pg/nodes/__examples__/basic/keystore.ts +38 -0
- package/pg/nodes/executeInSandbox.ts +32 -0
- package/pg/nodes/index.ts +3 -0
- package/pg/nodes/nodes.css +69 -0
- package/pg/nodes/nodes.html +6 -0
- package/pg/nodes/nodes.ts +1240 -0
- package/pg/nodes copy/nodes.css +41 -0
- package/pg/nodes copy/nodes.html +1 -0
- package/pg/overlayContextMenu/__examples__/filter/filter.ts +87 -0
- package/pg/overlayContextMenu/overlayContextMenu.css +14 -0
- package/pg/overlayContextMenu/overlayContextMenu.html +4 -3
- package/pg/overlayContextMenu/overlayContextMenu.ts +6 -2
- package/pg/overlayContextMenu/overlayContextMenu.ts.tmp.37088.530ff1c0a321 +0 -0
- package/pg/overlayMenu/overlayMenu.css +3 -1
- package/pg/overlayMenu/overlayMenu.ts +11 -2
- package/pg/overlayMenu/overlayMenu.ts.tmp.44252.9bb22fd59ede +80 -0
- package/pg/overlaySelectMenu/overlaySelectMenu.css +2 -0
- package/pg/overlaySubMenu/overlaySubMenu.css +8 -6
- package/pg/poco/README.md +22 -0
- package/pg/poco/__examples__/basic/basic.html +11 -0
- package/pg/poco/__examples__/basic/basic.ts +95 -0
- package/pg/poco/__examples__/basic/examples.ts +258 -0
- package/pg/poco/__examples__/basic/myFont.ts +1300 -0
- package/pg/poco/index.ts +3 -0
- package/pg/poco/mockPoco.ts +640 -0
- package/pg/poco/poco.css +7 -0
- package/pg/poco/poco.html +1 -0
- package/pg/poco/poco.ts +75 -0
- package/pg/popover/README.md +47 -0
- package/pg/popover/__examples__/basic/basic.html +3 -0
- package/pg/popover/popover.html +3 -0
- package/pg/popover/popover.ts +44 -0
- package/pg/popoverAutocomplete/__examples__/basic/basic.html +4 -0
- package/pg/popoverAutocomplete/__examples__/basic/basic.ts +46 -0
- package/pg/popoverAutocomplete/popoverAutocomplete.css +25 -0
- package/pg/popoverAutocomplete/popoverAutocomplete.html +1 -0
- package/pg/popoverAutocomplete/popoverAutocomplete.ts +134 -0
- package/pg/popoverAutocompleteItem/popoverAutocompleteItem.css +69 -0
- package/pg/popoverAutocompleteItem/popoverAutocompleteItem.ts +90 -0
- package/pg/table/__examples__/basic/basic.ts +1 -0
- package/pg/tableCellFileLocal/tableCellFileLocal.html +3 -0
- package/pg/tableCellFileLocal/tableCellFileLocal.html.tmp.20484.68ee2e30710e +3 -0
- package/pg/tableCellFileLocal/tableCellFileLocal.ts +44 -0
- package/pg/tableCellSelect/tableCellSelect.html +3 -0
- package/pg/tableCellSelect/tableCellSelect.html.tmp.41808.99c1a6f14340 +3 -0
- package/pg/tableCellSelect/tableCellSelect.ts +51 -0
- package/pg/tree/__examples__/basic/basic.ts +12 -5
- package/pg/tree/tree.html +1 -1
- package/pg/tree/tree.ts +385 -227
- package/pg/tree/tree.ts.tmp.38376.07d376a04f41 +343 -0
- package/pg/tree/tree.ts.tmp.38376.2a04599ade9f +361 -0
- package/pg/tree/tree.ts.tmp.38376.6cb2c663d476 +0 -0
- package/pg/tree/tree.ts.tmp.38376.ac5af1aff2b0 +393 -0
- package/pg/tree/tree.ts.tmp.38376.ac9acfc4f95f +324 -0
- package/pg/treeItem/treeItem.ts +536 -558
- package/pg/treeItem/treeItem.ts.tmp.38376.145d3121d648 +530 -0
- package/pg/treeItem/treeItem.ts.tmp.38376.b5224d48e5c0 +0 -0
|
@@ -13,7 +13,7 @@ interface Options {
|
|
|
13
13
|
scale?: number;
|
|
14
14
|
offsetX?: number;
|
|
15
15
|
offsetY?: number;
|
|
16
|
-
include?: number[];
|
|
16
|
+
include?: number[][];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function toIndex(x: number, y: number, width: number) {
|
|
@@ -21,10 +21,10 @@ export function toIndex(x: number, y: number, width: number) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Convert a 2d array to
|
|
24
|
+
* Convert a 2d array to SVG paths.
|
|
25
25
|
* @param data
|
|
26
26
|
* @param options
|
|
27
|
-
* @returns path
|
|
27
|
+
* @returns array of path strings, one per include grouping
|
|
28
28
|
*/
|
|
29
29
|
export default function bitmaskToPath(data: number[] | number[][], options: Options = {}) {
|
|
30
30
|
|
|
@@ -34,10 +34,10 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
34
34
|
scale = 1,
|
|
35
35
|
offsetX = 0,
|
|
36
36
|
offsetY = 0,
|
|
37
|
-
include = [1];
|
|
37
|
+
include: number[][] = [[1]];
|
|
38
38
|
|
|
39
39
|
if (options.width) {
|
|
40
|
-
bitmask = data as number[];
|
|
40
|
+
bitmask = data as number[];
|
|
41
41
|
width = options.width;
|
|
42
42
|
height = bitmask.length / width;
|
|
43
43
|
if (height % 1 !== 0) {
|
|
@@ -67,28 +67,38 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
67
67
|
include = options.include;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
const groupCount = include.length;
|
|
71
71
|
const newWidth = width + 2;
|
|
72
72
|
const newHeight = height + 2;
|
|
73
|
-
const bm = Array(newWidth * newHeight).fill(0);
|
|
74
73
|
|
|
75
|
-
// BM is just shifted over (1, 1) for the padding
|
|
76
74
|
function BMXYToIndex(x: number, y: number) {
|
|
77
75
|
return (y + 1) * newWidth + (x + 1);
|
|
78
76
|
}
|
|
79
77
|
|
|
78
|
+
// Build value → group bitmask lookup
|
|
79
|
+
const valueToGroupBits = new Map<number, number>();
|
|
80
|
+
for (let g = 0; g < groupCount; ++g) {
|
|
81
|
+
for (const val of include[g]) {
|
|
82
|
+
valueToGroupBits.set(val, (valueToGroupBits.get(val) ?? 0) | (1 << g));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Single pass: build padded cellMask where each cell stores a bitfield of group membership
|
|
87
|
+
const cellMask = new Int32Array(newWidth * newHeight);
|
|
80
88
|
for (let y = 0; y < height; ++y) {
|
|
81
89
|
for (let x = 0; x < width; ++x) {
|
|
82
|
-
|
|
90
|
+
const bits = valueToGroupBits.get(bitmask[toIndex(x, y, width)]) ?? 0;
|
|
91
|
+
if (bits) {
|
|
92
|
+
cellMask[BMXYToIndex(x, y)] = bits;
|
|
93
|
+
}
|
|
83
94
|
}
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
//
|
|
97
|
+
// Allocate per-group edge structures upfront
|
|
87
98
|
const edgeXCount = width * (height + 1);
|
|
88
99
|
const edgeYCount = (width + 1) * height;
|
|
89
100
|
const edgeCount = edgeXCount + edgeYCount;
|
|
90
101
|
|
|
91
|
-
const edges = Array(edgeCount).fill(0).map(() => ({ x: 0, y: 0, next: undefined })) as Edge[];
|
|
92
102
|
function EdgeXIndex(x: number, y: number) {
|
|
93
103
|
return y * width + x;
|
|
94
104
|
}
|
|
@@ -96,121 +106,145 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
96
106
|
return edgeXCount + y * (width + 1) + x;
|
|
97
107
|
}
|
|
98
108
|
|
|
99
|
-
const
|
|
109
|
+
const allEdges: Edge[][] = [];
|
|
110
|
+
const allContours: Set<Edge>[] = [];
|
|
111
|
+
for (let g = 0; g < groupCount; ++g) {
|
|
112
|
+
allEdges.push(Array(edgeCount).fill(0).map(() => ({ x: 0, y: 0, next: undefined })) as Edge[]);
|
|
113
|
+
allContours.push(new Set<Edge>());
|
|
114
|
+
}
|
|
100
115
|
|
|
101
|
-
|
|
116
|
+
// Helper: check group membership via cellMask bit
|
|
117
|
+
function isSet(x: number, y: number, bit: number) {
|
|
118
|
+
return (cellMask[BMXYToIndex(x, y)] & bit) !== 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function SetEdge(contours: Set<Edge>, edge: Edge, x: number, y: number) {
|
|
102
122
|
edge.x = x;
|
|
103
123
|
edge.y = y;
|
|
104
|
-
|
|
124
|
+
contours.add(edge);
|
|
105
125
|
}
|
|
106
126
|
|
|
107
|
-
function UnionGroup(edge: Edge) {
|
|
127
|
+
function UnionGroup(contours: Set<Edge>, edge: Edge) {
|
|
108
128
|
for (var itr = edge.next; itr !== undefined && itr !== edge; itr = itr.next) {
|
|
109
|
-
|
|
129
|
+
contours.delete(itr);
|
|
110
130
|
}
|
|
111
131
|
if (itr !== undefined) {
|
|
112
|
-
|
|
132
|
+
contours.add(edge);
|
|
113
133
|
}
|
|
114
134
|
}
|
|
115
135
|
|
|
136
|
+
// Single pass edge detection for all groups
|
|
116
137
|
for (let y = 0; y < height; ++y) {
|
|
117
138
|
for (let x = 0; x < width; ++x) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
139
|
+
const myMask = cellMask[BMXYToIndex(x, y)];
|
|
140
|
+
if (myMask === 0) continue;
|
|
141
|
+
|
|
142
|
+
for (let g = 0; g < groupCount; ++g) {
|
|
143
|
+
const groupBit = 1 << g;
|
|
144
|
+
if ((myMask & groupBit) === 0) continue;
|
|
145
|
+
|
|
146
|
+
const edges = allEdges[g];
|
|
147
|
+
const contours = allContours[g];
|
|
148
|
+
|
|
149
|
+
if (!isSet(x - 1, y, groupBit)) {
|
|
121
150
|
const edge = edges[EdgeYIndex(x, y)];
|
|
122
|
-
SetEdge(edge, x, y + 1);
|
|
123
|
-
if (
|
|
151
|
+
SetEdge(contours, edge, x, y + 1);
|
|
152
|
+
if (isSet(x - 1, y - 1, groupBit)) {
|
|
124
153
|
edge.next = edges[EdgeXIndex(x - 1, y)];
|
|
125
|
-
} else if (
|
|
154
|
+
} else if (isSet(x, y - 1, groupBit)) {
|
|
126
155
|
edge.next = edges[EdgeYIndex(x, y - 1)];
|
|
127
156
|
} else {
|
|
128
157
|
edge.next = edges[EdgeXIndex(x, y)];
|
|
129
158
|
}
|
|
130
|
-
UnionGroup(edge);
|
|
159
|
+
UnionGroup(contours, edge);
|
|
131
160
|
}
|
|
132
|
-
|
|
133
|
-
if (right === 0) {
|
|
161
|
+
if (!isSet(x + 1, y, groupBit)) {
|
|
134
162
|
const edge = edges[EdgeYIndex(x + 1, y)];
|
|
135
|
-
SetEdge(edge, x + 1, y);
|
|
136
|
-
if (
|
|
163
|
+
SetEdge(contours, edge, x + 1, y);
|
|
164
|
+
if (isSet(x + 1, y + 1, groupBit)) {
|
|
137
165
|
edge.next = edges[EdgeXIndex(x + 1, y + 1)];
|
|
138
|
-
} else if (
|
|
166
|
+
} else if (isSet(x, y + 1, groupBit)) {
|
|
139
167
|
edge.next = edges[EdgeYIndex(x + 1, y + 1)];
|
|
140
168
|
} else {
|
|
141
169
|
edge.next = edges[EdgeXIndex(x, y + 1)];
|
|
142
170
|
}
|
|
143
|
-
UnionGroup(edge);
|
|
171
|
+
UnionGroup(contours, edge);
|
|
144
172
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (bm[BMXYToIndex(x + 1, y - 1)]) {
|
|
173
|
+
if (!isSet(x, y - 1, groupBit)) {
|
|
174
|
+
const edge = edges[EdgeXIndex(x, y)];
|
|
175
|
+
SetEdge(contours, edge, x, y);
|
|
176
|
+
if (isSet(x + 1, y - 1, groupBit)) {
|
|
150
177
|
edge.next = edges[EdgeYIndex(x + 1, y - 1)];
|
|
151
|
-
} else if (
|
|
178
|
+
} else if (isSet(x + 1, y, groupBit)) {
|
|
152
179
|
edge.next = edges[EdgeXIndex(x + 1, y)];
|
|
153
180
|
} else {
|
|
154
181
|
edge.next = edges[EdgeYIndex(x + 1, y)];
|
|
155
182
|
}
|
|
156
|
-
UnionGroup(edge);
|
|
183
|
+
UnionGroup(contours, edge);
|
|
157
184
|
}
|
|
158
|
-
|
|
159
|
-
if (bottom === 0) {
|
|
185
|
+
if (!isSet(x, y + 1, groupBit)) {
|
|
160
186
|
const edge = edges[EdgeXIndex(x, y + 1)];
|
|
161
|
-
SetEdge(edge, x + 1, y + 1);
|
|
162
|
-
if (
|
|
187
|
+
SetEdge(contours, edge, x + 1, y + 1);
|
|
188
|
+
if (isSet(x - 1, y + 1, groupBit)) {
|
|
163
189
|
edge.next = edges[EdgeYIndex(x, y + 1)];
|
|
164
|
-
} else if (
|
|
190
|
+
} else if (isSet(x - 1, y, groupBit)) {
|
|
165
191
|
edge.next = edges[EdgeXIndex(x - 1, y + 1)];
|
|
166
192
|
} else {
|
|
167
193
|
edge.next = edges[EdgeYIndex(x, y)];
|
|
168
194
|
}
|
|
169
|
-
UnionGroup(edge);
|
|
195
|
+
UnionGroup(contours, edge);
|
|
170
196
|
}
|
|
171
197
|
}
|
|
172
198
|
}
|
|
173
199
|
}
|
|
174
200
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
// Per-group post-processing: type assignment, compression, path building
|
|
202
|
+
const paths: string[] = [];
|
|
203
|
+
|
|
204
|
+
for (let g = 0; g < groupCount; ++g) {
|
|
205
|
+
const contours = allContours[g];
|
|
206
|
+
|
|
207
|
+
for (const edge of contours) {
|
|
208
|
+
let itr = edge;
|
|
209
|
+
do {
|
|
210
|
+
if (itr.next) {
|
|
211
|
+
itr.next.type = itr.x == itr?.next?.x ? 'V' : 'H';
|
|
212
|
+
itr = itr.next;
|
|
213
|
+
}
|
|
214
|
+
} while (itr !== edge);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (let edge of contours) {
|
|
218
|
+
let itr = edge;
|
|
219
|
+
do {
|
|
220
|
+
if (itr.type != itr.next?.type) {
|
|
221
|
+
while (itr.next?.type == itr.next?.next?.type) {
|
|
222
|
+
if (itr.next === edge) {
|
|
223
|
+
contours.delete(edge);
|
|
224
|
+
edge = itr.next.next as Edge;
|
|
225
|
+
contours.add(edge);
|
|
226
|
+
}
|
|
227
|
+
itr.next = itr.next?.next;
|
|
195
228
|
}
|
|
196
|
-
|
|
229
|
+
}
|
|
230
|
+
itr = itr.next as Edge;
|
|
231
|
+
} while (itr !== edge);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let path = '';
|
|
235
|
+
for (const edge of contours) {
|
|
236
|
+
path += `M${edge.x * scale},${edge.y * scale}`;
|
|
237
|
+
for (var itr = edge.next; itr != edge; itr = itr?.next) {
|
|
238
|
+
if (itr?.type == 'H') {
|
|
239
|
+
path += `H${(itr?.x * scale) + offsetX}`;
|
|
240
|
+
} else if (itr?.type == 'V') {
|
|
241
|
+
path += `V${(itr?.y * scale) + offsetY}`;
|
|
197
242
|
}
|
|
198
243
|
}
|
|
199
|
-
|
|
200
|
-
} while (itr !== edge);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
let path = '';
|
|
204
|
-
for (const edge of groups) {
|
|
205
|
-
path += `M${edge.x * scale},${edge.y * scale}`;
|
|
206
|
-
for (var itr = edge.next; itr != edge; itr = itr?.next) {
|
|
207
|
-
if (itr?.type == 'H') {
|
|
208
|
-
path += `H${(itr?.x * scale) + offsetX}`;
|
|
209
|
-
} else if (itr?.type == 'V') {
|
|
210
|
-
path += `V${(itr?.y * scale) + offsetY}`;
|
|
211
|
-
}
|
|
244
|
+
path += 'Z';
|
|
212
245
|
}
|
|
213
|
-
path
|
|
246
|
+
paths.push(path);
|
|
214
247
|
}
|
|
215
|
-
|
|
248
|
+
|
|
249
|
+
return paths;
|
|
216
250
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import getLinePixels from './getLinePixels';
|
|
2
|
+
import { Pixel } from './constants';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns all pixels inside (and on the border of) a freehand lasso path.
|
|
6
|
+
*
|
|
7
|
+
* The path is a sequence of grid-cell coordinates visited during the drag.
|
|
8
|
+
* Consecutive points are connected with Bresenham lines to form the outline,
|
|
9
|
+
* and the interior is filled with an even-odd scanline rule.
|
|
10
|
+
*/
|
|
11
|
+
export default function getLassoPixels(path: [number, number][]): Pixel[] {
|
|
12
|
+
if (path.length < 3) { return []; }
|
|
13
|
+
|
|
14
|
+
const result = new Map<string, Pixel>();
|
|
15
|
+
const n = path.length;
|
|
16
|
+
|
|
17
|
+
// Outline: Bresenham lines between consecutive path points, closing back to start
|
|
18
|
+
for (let i = 0; i < n; i++) {
|
|
19
|
+
const [x0, y0] = path[i];
|
|
20
|
+
const [x1, y1] = path[(i + 1) % n];
|
|
21
|
+
for (const { x, y } of getLinePixels(x0, y0, x1, y1)) {
|
|
22
|
+
result.set(`${x},${y}`, { x, y });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Scanline interior fill — test at row + 0.5 to avoid vertex ambiguities
|
|
27
|
+
const minY = Math.min(...path.map(([, y]) => y));
|
|
28
|
+
const maxY = Math.max(...path.map(([, y]) => y));
|
|
29
|
+
|
|
30
|
+
for (let row = minY; row <= maxY; row++) {
|
|
31
|
+
const intersections: number[] = [];
|
|
32
|
+
for (let i = 0; i < n; i++) {
|
|
33
|
+
const [x0, y0] = path[i];
|
|
34
|
+
const [x1, y1] = path[(i + 1) % n];
|
|
35
|
+
if ((y0 <= row && y1 > row) || (y1 <= row && y0 > row)) {
|
|
36
|
+
const t = (row + 0.5 - y0) / (y1 - y0);
|
|
37
|
+
intersections.push(x0 + t * (x1 - x0));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
intersections.sort((a, b) => a - b);
|
|
41
|
+
for (let i = 0; i + 1 < intersections.length; i += 2) {
|
|
42
|
+
const xStart = Math.ceil(intersections[i]);
|
|
43
|
+
const xEnd = Math.floor(intersections[i + 1]);
|
|
44
|
+
for (let x = xStart; x <= xEnd; x++) {
|
|
45
|
+
result.set(`${x},${row}`, { x, y: row });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Array.from(result.values());
|
|
51
|
+
}
|
|
@@ -40,6 +40,10 @@ export default class PgInputSelect extends HTMLElement {
|
|
|
40
40
|
|
|
41
41
|
#menuOpen = false;
|
|
42
42
|
async #handleClick() {
|
|
43
|
+
if (this.options.length === 0) {
|
|
44
|
+
// No options
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
43
47
|
if (this.#menuOpen) { return; }
|
|
44
48
|
this.#menuOpen = true;
|
|
45
49
|
const result = await PgOverlaySelectMenu.open({
|
|
@@ -48,7 +52,7 @@ export default class PgInputSelect extends HTMLElement {
|
|
|
48
52
|
value: this.options.find(x => x.value === this.value) ?? null,
|
|
49
53
|
items: this.options,
|
|
50
54
|
});
|
|
51
|
-
if (result !==
|
|
55
|
+
if (result && result.value !== null) {
|
|
52
56
|
this.dispatchEvent(new CustomEvent('change', {
|
|
53
57
|
detail: {
|
|
54
58
|
value: result.value,
|
package/pg/inputText/README.md
CHANGED
|
@@ -30,9 +30,9 @@ import { PgInputText } from '@pictogrammers/components/pgInputText';
|
|
|
30
30
|
|
|
31
31
|
| CSS Variables | Default | Description |
|
|
32
32
|
| ------------------- | --------- | ----------- |
|
|
33
|
-
| `--pg-
|
|
34
|
-
| `--pg-
|
|
35
|
-
| `--pg-
|
|
36
|
-
| `--pg-
|
|
37
|
-
| `--pg-
|
|
38
|
-
| `--pg-
|
|
33
|
+
| `--pg-input-text-padding-inline` | `0.5rem` | Padding inline. |
|
|
34
|
+
| `--pg-input-text-padding-inline-start` | `0.5rem` | Padding inline start. |
|
|
35
|
+
| `--pg-input-text-padding-inline-end` | `0.5rem` | Padding inline end. |
|
|
36
|
+
| `--pg-input-text-padding-block` | `0.25rem` | Padding block. |
|
|
37
|
+
| `--pg-input-text-padding-block-start` | `0.25rem` | Padding block start. |
|
|
38
|
+
| `--pg-input-text-padding-block-end` | `0.25rem` | Padding block end. |
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
[part=input] {
|
|
7
7
|
border: 1px solid var(--pg-input-text-border-color, #453C4F);
|
|
8
|
-
border-radius: 0.125rem;
|
|
8
|
+
border-radius: var(--pg-input-border-radius, 0.125rem);
|
|
9
9
|
padding-inline-start: var(--pg-input-text-padding-inline-start, var(--pg-input-text-padding-inline, 0.5rem));
|
|
10
10
|
padding-inline-end: var(--pg-input-text-padding-inline-end, var(--pg-input-text-padding-inline, 0.5rem));
|
|
11
11
|
padding-block-start: var(--pg-input-text-padding-block-start, var(--pg-input-text-padding-block, 0.25rem));
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
- var(--pg-input-text-padding-inline-end, var(--pg-input-text-padding-inline, 0.5rem))
|
|
27
27
|
- 2px
|
|
28
28
|
);
|
|
29
|
-
background-color:
|
|
29
|
+
background-color: #FFF;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
[part=input]:read-only {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `<pg-input-text>`
|
|
2
|
+
|
|
3
|
+
The `pg-input-text` component creates an input that accepts text input.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import '@pictogrammers/components/pgInputText';
|
|
7
|
+
import { PgInputText } from '@pictogrammers/components/pgInputText';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<pg-input-text value="50"></pg-input-text>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Attributes
|
|
15
|
+
|
|
16
|
+
| Attributes | Tested | Description |
|
|
17
|
+
| ----------- | -------- | ----------- |
|
|
18
|
+
| name | | Unique name in `pg-form` |
|
|
19
|
+
| value | | Field value |
|
|
20
|
+
| placeholder | | Placeholder text |
|
|
21
|
+
|
|
22
|
+
## Events
|
|
23
|
+
|
|
24
|
+
| Events | Tested | Description |
|
|
25
|
+
| ---------- | -------- | ----------- |
|
|
26
|
+
| change | | `{ detail: { value }` |
|
|
27
|
+
| input | | `{ detail: { value }` |
|
|
28
|
+
|
|
29
|
+
## CSS Variables
|
|
30
|
+
|
|
31
|
+
| CSS Variables | Default | Description |
|
|
32
|
+
| ------------------- | --------- | ----------- |
|
|
33
|
+
| `--pg-button-padding-inline` | `0.5rem` | Padding inline. |
|
|
34
|
+
| `--pg-button-padding-inline-start` | `0.5rem` | Padding inline start. |
|
|
35
|
+
| `--pg-button-padding-inline-end` | `0.5rem` | Padding inline end. |
|
|
36
|
+
| `--pg-button-padding-block` | `0.25rem` | Padding block. |
|
|
37
|
+
| `--pg-button-padding-block-start` | `0.25rem` | Padding block start. |
|
|
38
|
+
| `--pg-button-padding-block-end` | `0.25rem` | Padding block end. |
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
font-family: var(--pg-font-family, system-ui);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
[part=input] {
|
|
7
|
+
border: 1px solid var(--pg-input-text-border-color, #453C4F);
|
|
8
|
+
border-radius: 0.125rem;
|
|
9
|
+
padding-inline-start: var(--pg-input-text-padding-inline-start, var(--pg-input-text-padding-inline, 0.5rem));
|
|
10
|
+
padding-inline-end: var(--pg-input-text-padding-inline-end, var(--pg-input-text-padding-inline, 0.5rem));
|
|
11
|
+
padding-block-start: var(--pg-input-text-padding-block-start, var(--pg-input-text-padding-block, 0.25rem));
|
|
12
|
+
padding-block-end: var(--pg-input-text-padding-block-start, var(--pg-input-text-padding-block, 0.25rem));
|
|
13
|
+
font-family: var(--pg-input-text-font-family, var(--pg-font-family));
|
|
14
|
+
font-size: 1rem;
|
|
15
|
+
outline: none;
|
|
16
|
+
field-sizing: content;
|
|
17
|
+
min-width: calc(
|
|
18
|
+
100%
|
|
19
|
+
- var(--pg-input-text-padding-inline-start, var(--pg-input-text-padding-inline, 0.5rem))
|
|
20
|
+
- var(--pg-input-text-padding-inline-end, var(--pg-input-text-padding-inline, 0.5rem))
|
|
21
|
+
- 2px
|
|
22
|
+
);
|
|
23
|
+
max-width: calc(
|
|
24
|
+
var(--pg-input-text-max-width, 100%)
|
|
25
|
+
- var(--pg-input-text-padding-inline-start, var(--pg-input-text-padding-inline, 0.5rem))
|
|
26
|
+
- var(--pg-input-text-padding-inline-end, var(--pg-input-text-padding-inline, 0.5rem))
|
|
27
|
+
- 2px
|
|
28
|
+
);
|
|
29
|
+
background-color: transparent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[part=input]:read-only {
|
|
33
|
+
border-color: transparent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[part=input]:active {
|
|
37
|
+
box-shadow: 0 0 0 3px var(--pg-input-text-active-glow, rgb(79, 143, 249, 0.6));
|
|
38
|
+
}
|
|
39
|
+
[part=input]:focus {
|
|
40
|
+
box-shadow: 0 0 0 3px var(--pg-input-text-focus-glow, rgb(79, 143, 249, 0.5));
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<input part="input" type="text" />
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { selectComponent, getProps } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import './inputText';
|
|
4
|
+
import PgInputText from './inputText';
|
|
5
|
+
|
|
6
|
+
const PG_INPUT_TEXT = 'pg-input-text';
|
|
7
|
+
|
|
8
|
+
describe('pg-input-text', () => {
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
var c = document.createElement(PG_INPUT_TEXT);
|
|
12
|
+
document.body.appendChild(c);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
while (document.body.firstChild) {
|
|
17
|
+
document.body.removeChild(document.body.firstChild);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should be registered', () => {
|
|
22
|
+
expect(customElements.get(PG_INPUT_TEXT)).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should only expose known props', () => {
|
|
26
|
+
const props = getProps(PG_INPUT_TEXT);
|
|
27
|
+
expect(props.length).toBe(3);
|
|
28
|
+
expect(props).toContain('name');
|
|
29
|
+
expect(props).toContain('value');
|
|
30
|
+
expect(props).toContain('placeholder');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should default value to empty', () => {
|
|
34
|
+
const component = selectComponent<PgInputText>(PG_INPUT_TEXT);
|
|
35
|
+
const { $input } = component;
|
|
36
|
+
expect($input.value).toEqual('');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should set value to "Hello World!"', () => {
|
|
40
|
+
const component = selectComponent<PgInputText>(PG_INPUT_TEXT);
|
|
41
|
+
component.value = 'Hello World!';
|
|
42
|
+
const { $input } = component;
|
|
43
|
+
expect($input.value).toEqual('Hello World!');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should default placeholder to empty', () => {
|
|
47
|
+
const component = selectComponent<PgInputText>(PG_INPUT_TEXT);
|
|
48
|
+
const { $input } = component;
|
|
49
|
+
expect($input.placeholder).toEqual('');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should set placeholder to "Hello World!"', () => {
|
|
53
|
+
const component = selectComponent<PgInputText>(PG_INPUT_TEXT);
|
|
54
|
+
component.placeholder = 'Hello World!';
|
|
55
|
+
const { $input } = component;
|
|
56
|
+
expect($input.placeholder).toEqual('Hello World!');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import template from './inputText.html';
|
|
4
|
+
import style from './inputText.css';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'pg-input-text',
|
|
8
|
+
style,
|
|
9
|
+
template
|
|
10
|
+
})
|
|
11
|
+
export default class PgInputText extends HTMLElement {
|
|
12
|
+
@Prop() name: string = '';
|
|
13
|
+
@Prop() value: string = '';
|
|
14
|
+
@Prop() placeholder: string = '';
|
|
15
|
+
@Prop() readOnly: boolean = false;
|
|
16
|
+
|
|
17
|
+
@Part() $input: HTMLInputElement;
|
|
18
|
+
|
|
19
|
+
connectedCallback() {
|
|
20
|
+
this.$input.addEventListener('input', this.handleInput.bind(this));
|
|
21
|
+
this.$input.addEventListener('change', this.handleChange.bind(this));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
focus() {
|
|
25
|
+
this.$input.focus();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
skipValue = false;
|
|
29
|
+
|
|
30
|
+
render(changes) {
|
|
31
|
+
if (changes.value && !this.skipValue) {
|
|
32
|
+
this.$input.value = this.value;
|
|
33
|
+
}
|
|
34
|
+
if (changes.placeholder) {
|
|
35
|
+
this.$input.placeholder = this.placeholder;
|
|
36
|
+
}
|
|
37
|
+
if (changes.readOnly) {
|
|
38
|
+
this.$input.readOnly = this.readOnly;
|
|
39
|
+
}
|
|
40
|
+
this.skipValue = false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
handleInput(e) {
|
|
44
|
+
e.stopPropagation();
|
|
45
|
+
this.skipValue = true;
|
|
46
|
+
this.value = e.target.value;
|
|
47
|
+
this.dispatchEvent(
|
|
48
|
+
new CustomEvent('input', {
|
|
49
|
+
detail: {
|
|
50
|
+
value: e.target.value,
|
|
51
|
+
name: e.name
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
handleChange(e) {
|
|
58
|
+
this.dispatchEvent(
|
|
59
|
+
new CustomEvent('change', {
|
|
60
|
+
detail: {
|
|
61
|
+
value: e.target.value,
|
|
62
|
+
name: e.name
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# `<pg-input-textarea>`
|
|
2
|
+
|
|
3
|
+
The `pg-input-textarea` component creates an input that accepts multiline text. Additionally it can resize.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import '@pictogrammers/components/pgInputTextarea';
|
|
7
|
+
import { PgInputTextarea } from '@pictogrammers/components/pgInputTextarea';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<pg-input-textarea value="50"></pg-input-textarea>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Attributes
|
|
15
|
+
|
|
16
|
+
| Attributes | Tested | Description |
|
|
17
|
+
| ----------- | -------- | ----------- |
|
|
18
|
+
| name | | Unique name in `pg-form` |
|
|
19
|
+
| value | | Field value |
|
|
20
|
+
| placeholder | | Placeholder text |
|
|
21
|
+
|
|
22
|
+
## Events
|
|
23
|
+
|
|
24
|
+
| Events | Tested | Description |
|
|
25
|
+
| ---------- | -------- | ----------- |
|
|
26
|
+
| change | | `{ detail: { value }` |
|
|
27
|
+
| input | | `{ detail: { value }` |
|
|
28
|
+
| caret | | `{ detail: { column, row }}` |
|
|
29
|
+
|
|
30
|
+
## CSS Variables
|
|
31
|
+
|
|
32
|
+
| CSS Variables | Default | Description |
|
|
33
|
+
| ------------------- | --------- | ----------- |
|
|
34
|
+
| `--pg-input-textarea-padding-inline` | `0.5rem` | Padding inline. |
|
|
35
|
+
| `--pg-input-textarea-padding-inline-start` | `0.5rem` | Padding inline start. |
|
|
36
|
+
| `--pg-input-textarea-padding-inline-end` | `0.5rem` | Padding inline end. |
|
|
37
|
+
| `--pg-input-textarea-padding-block` | `0.25rem` | Padding block. |
|
|
38
|
+
| `--pg-input-textarea-padding-block-start` | `0.25rem` | Padding block start. |
|
|
39
|
+
| `--pg-input-textarea-padding-block-end` | `0.25rem` | Padding block end. |
|