@pictogrammers/components 0.5.24 → 0.5.26
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/inputPixelEditor/__examples__/basic/basic.ts +3 -0
- package/pg/inputPixelEditor/inputPixelEditor.ts +203 -78
- package/pg/inputTextarea copy/README.md +0 -39
- package/pg/inputTextarea copy/inputTextarea.css +0 -41
- package/pg/inputTextarea copy/inputTextarea.html +0 -1
- package/pg/inputTextarea copy/inputTextarea.spec.ts +0 -59
- package/pg/inputTextarea copy/inputTextarea.ts +0 -79
package/package.json
CHANGED
|
@@ -184,6 +184,9 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
|
|
|
184
184
|
}
|
|
185
185
|
});
|
|
186
186
|
// Layers
|
|
187
|
+
// The input no longer seeds a default layer; create the one shown
|
|
188
|
+
// as already-selected in the layers table below.
|
|
189
|
+
this.$input.addLayer({ name: 'Layer 1', type: 'pixel' });
|
|
187
190
|
this.$layers.columns = [{
|
|
188
191
|
label: 'Name',
|
|
189
192
|
key: 'name',
|
|
@@ -173,7 +173,6 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
173
173
|
#colors: Color[] = [
|
|
174
174
|
[0, 0, 0, 0],
|
|
175
175
|
[0, 0, 0, 1],
|
|
176
|
-
[255, 0, 255, 1]
|
|
177
176
|
];
|
|
178
177
|
#baseLayer: HTMLCanvasElement;
|
|
179
178
|
#baseLayerContext: CanvasRenderingContext2D;
|
|
@@ -282,16 +281,14 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
282
281
|
}
|
|
283
282
|
this.$selection.setAttribute('viewBox', `0 0 ${this.width * this.size} ${this.height * this.size}`);
|
|
284
283
|
if (this.#reset) {
|
|
284
|
+
// No default layer: callers (e.g. addLayer()) are expected to build
|
|
285
|
+
// up #layers/#data explicitly. A seeded "Layer 1" here used to sit
|
|
286
|
+
// at index 0 unknown to callers that always addLayer() their own
|
|
287
|
+
// first layer too, leaving a phantom extra layer that (combined with
|
|
288
|
+
// setData's indexing) silently absorbed every layer's paint.
|
|
285
289
|
this.#layer = [0];
|
|
286
|
-
this.#layers = [
|
|
287
|
-
|
|
288
|
-
type: 'pixel',
|
|
289
|
-
export: true,
|
|
290
|
-
locked: false,
|
|
291
|
-
visible: true,
|
|
292
|
-
opacity: 1
|
|
293
|
-
}];
|
|
294
|
-
this.#data = [fillGrid(this.width, this.height)];
|
|
290
|
+
this.#layers = [];
|
|
291
|
+
this.#data = [];
|
|
295
292
|
this.#export = fillGrid(this.width, this.height);
|
|
296
293
|
this.#selectionPreview = fillGrid(this.width, this.height);
|
|
297
294
|
this.#selection = fillGrid(this.width, this.height);
|
|
@@ -305,29 +302,46 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
305
302
|
}
|
|
306
303
|
|
|
307
304
|
/**
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
305
|
+
* Grow or clip a cached grid in place so it is exactly width x height.
|
|
306
|
+
* New rows/cells are filled with 0. Each grid is resized independently
|
|
307
|
+
* so multi-layer grids never drift out of sync with each other (a prior
|
|
308
|
+
* version resized `#export`/`#selection`/`#selectionPreview` once per
|
|
309
|
+
* layer instead of once per grid, and used a single layer's length to
|
|
310
|
+
* decide whether other layers needed growing, which could leave a layer
|
|
311
|
+
* short and let #redraw read past its real length).
|
|
312
|
+
* @param grid 2d grid to resize in place
|
|
313
|
+
* @param width target width
|
|
314
|
+
* @param height target height
|
|
311
315
|
*/
|
|
312
|
-
#
|
|
313
|
-
if (grid.length >
|
|
314
|
-
grid.length =
|
|
316
|
+
#resizeGrid(grid: number[][], width: number, height: number) {
|
|
317
|
+
if (grid.length > height) {
|
|
318
|
+
grid.length = height;
|
|
315
319
|
}
|
|
316
|
-
for (let y = 0; y <
|
|
317
|
-
if (grid[y]
|
|
318
|
-
grid[y]
|
|
320
|
+
for (let y = 0; y < height; y++) {
|
|
321
|
+
if (!grid[y]) {
|
|
322
|
+
grid[y] = new Array(width).fill(0);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (grid[y].length > width) {
|
|
326
|
+
grid[y].length = width;
|
|
327
|
+
} else {
|
|
328
|
+
for (let x = grid[y].length; x < width; x++) {
|
|
329
|
+
grid[y].push(0);
|
|
330
|
+
}
|
|
319
331
|
}
|
|
320
332
|
}
|
|
321
333
|
}
|
|
322
334
|
|
|
323
335
|
#redraw() {
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
this.#
|
|
328
|
-
this.#
|
|
336
|
+
// Resize every cached grid to the current width/height so stale
|
|
337
|
+
// rows/columns are dropped when shrinking and missing rows/columns are
|
|
338
|
+
// backfilled with 0 when growing, before anything reads from them.
|
|
339
|
+
this.#data.forEach((layer) => this.#resizeGrid(layer, this.width, this.height));
|
|
340
|
+
this.#resizeGrid(this.#export, this.width, this.height);
|
|
341
|
+
this.#resizeGrid(this.#selection, this.width, this.height);
|
|
342
|
+
this.#resizeGrid(this.#selectionPreview, this.width, this.height);
|
|
329
343
|
// Drop selection pixels that now fall outside the canvas so the
|
|
330
|
-
// selection map stays in sync with the
|
|
344
|
+
// selection map stays in sync with the resized #selection grid.
|
|
331
345
|
this.#selectionPixels.forEach(([x, y], key) => {
|
|
332
346
|
if (x >= this.width || y >= this.height) {
|
|
333
347
|
this.#selectionPixels.delete(key);
|
|
@@ -337,33 +351,26 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
337
351
|
this.$selectionPath.classList.toggle('hide', true);
|
|
338
352
|
}
|
|
339
353
|
|
|
340
|
-
// Render individual pixels
|
|
341
|
-
|
|
342
|
-
|
|
354
|
+
// Render individual pixels, topmost non-transparent layer wins. A cell
|
|
355
|
+
// with no color on any layer still needs #setPixel if #export disagrees,
|
|
356
|
+
// so it actually clears to background — e.g. after removeColor/moveColor
|
|
357
|
+
// zero out cells that used to hold paint. Skipping that check (instead
|
|
358
|
+
// of just unconditionally calling #setPixel for every blank cell) keeps
|
|
359
|
+
// the common case — most cells are already blank on both sides — as
|
|
360
|
+
// cheap as it was before, since #setPixel does real canvas work per call.
|
|
343
361
|
for (let y = 0; y < this.height; y++) {
|
|
344
|
-
if (y >= data[0].length) {
|
|
345
|
-
for (let l = 0; l < layerCount; l++) {
|
|
346
|
-
data[l].push(new Array(this.width).fill(0));
|
|
347
|
-
this.#export.push(new Array(this.width).fill(0));
|
|
348
|
-
this.#selection.push(new Array(this.width).fill(0));
|
|
349
|
-
this.#selectionPreview.push(new Array(this.width).fill(0));
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
362
|
for (let x = 0; x < this.width; x++) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
this.#
|
|
357
|
-
|
|
358
|
-
this.#selectionPreview[y].push(0);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
for (let l = 0; l < layerCount; l++) {
|
|
362
|
-
if (data[l][y][x] !== 0) {
|
|
363
|
-
this.#setPixel(x, y, data[l][y][x]);
|
|
363
|
+
let painted = false;
|
|
364
|
+
for (let l = this.#data.length - 1; l >= 0; l--) {
|
|
365
|
+
if (this.#data[l][y][x] !== 0) {
|
|
366
|
+
this.#setPixel(x, y, this.#data[l][y][x], [l]);
|
|
367
|
+
painted = true;
|
|
364
368
|
break;
|
|
365
369
|
}
|
|
366
370
|
}
|
|
371
|
+
if (!painted && this.#export[y][x] !== 0) {
|
|
372
|
+
this.#setPixel(x, y, 0, [this.#layer[0]]);
|
|
373
|
+
}
|
|
367
374
|
}
|
|
368
375
|
}
|
|
369
376
|
}
|
|
@@ -548,15 +555,18 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
548
555
|
);
|
|
549
556
|
// preview layer
|
|
550
557
|
this.#previewLayerContext.clearRect(0, 0, actualWidth, actualHeight);
|
|
558
|
+
const center = this.size / 2;
|
|
559
|
+
const outerRadius = Math.max(1, this.size * 0.3);
|
|
560
|
+
const innerRadius = Math.max(0.5, outerRadius - 1);
|
|
551
561
|
pixels.forEach(({ x, y }) => {
|
|
552
562
|
this.#previewLayerContext.fillStyle = WHITE;
|
|
553
563
|
this.#previewLayerContext.beginPath();
|
|
554
|
-
this.#previewLayerContext.arc(x * totalSize +
|
|
564
|
+
this.#previewLayerContext.arc(x * totalSize + center, y * totalSize + center, outerRadius, 0, 2 * Math.PI);
|
|
555
565
|
this.#previewLayerContext.closePath();
|
|
556
566
|
this.#previewLayerContext.fill();
|
|
557
567
|
this.#previewLayerContext.fillStyle = '#1B79C8';
|
|
558
568
|
this.#previewLayerContext.beginPath();
|
|
559
|
-
this.#previewLayerContext.arc(x * totalSize +
|
|
569
|
+
this.#previewLayerContext.arc(x * totalSize + center, y * totalSize + center, innerRadius, 0, 2 * Math.PI);
|
|
560
570
|
this.#previewLayerContext.closePath();
|
|
561
571
|
this.#previewLayerContext.fill();
|
|
562
572
|
});
|
|
@@ -1224,7 +1234,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1224
1234
|
|
|
1225
1235
|
invert() {
|
|
1226
1236
|
// Only works with 2 colors
|
|
1227
|
-
if (this
|
|
1237
|
+
if (this.getActiveColors().length > 2) {
|
|
1228
1238
|
return;
|
|
1229
1239
|
}
|
|
1230
1240
|
iterateGrid(this.#data[this.#layer[0]], (x, y) => {
|
|
@@ -1432,30 +1442,129 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1432
1442
|
return this.#colors;
|
|
1433
1443
|
}
|
|
1434
1444
|
|
|
1445
|
+
/**
|
|
1446
|
+
* Replace the full palette, e.g. to sync an externally-managed palette
|
|
1447
|
+
* (a file-level color list) onto this editor. Does not remap existing
|
|
1448
|
+
* pixel data; callers are responsible for the new colors lining up with
|
|
1449
|
+
* indexes already painted on this editor.
|
|
1450
|
+
*
|
|
1451
|
+
* Clones `colors` (and each entry) so this editor's palette stays its
|
|
1452
|
+
* own array — callers commonly push the same list onto several editors
|
|
1453
|
+
* to keep them in sync, and sharing the array by reference would make
|
|
1454
|
+
* every one of those editors mutate the same underlying palette.
|
|
1455
|
+
* @param colors New palette
|
|
1456
|
+
*/
|
|
1457
|
+
setColors(colors: Color[]) {
|
|
1458
|
+
this.#colors = colors.map((color) => [...color] as Color);
|
|
1459
|
+
this.#redraw();
|
|
1460
|
+
this.#handleColorsChange();
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1435
1463
|
addColor(r: number, g: number, b: number, a: number) {
|
|
1436
1464
|
this.#colors.push([r, g, b, a]);
|
|
1465
|
+
this.#handleColorsChange();
|
|
1437
1466
|
}
|
|
1438
1467
|
|
|
1439
|
-
|
|
1468
|
+
/**
|
|
1469
|
+
* Remove a color from the palette. Index 0 (the background/empty color)
|
|
1470
|
+
* can't be removed. Pixels painted with the removed color become empty
|
|
1471
|
+
* (0); pixels painted with a later color shift down to match the new
|
|
1472
|
+
* palette indexes.
|
|
1473
|
+
* @param index Color index to remove
|
|
1474
|
+
*/
|
|
1475
|
+
removeColor(index: number) {
|
|
1476
|
+
if (index <= 0 || index >= this.#colors.length) { return; }
|
|
1440
1477
|
this.#colors.splice(index, 1);
|
|
1478
|
+
this.#data.forEach((layer) => {
|
|
1479
|
+
for (let y = 0; y < layer.length; y++) {
|
|
1480
|
+
for (let x = 0; x < layer[y].length; x++) {
|
|
1481
|
+
const value = layer[y][x];
|
|
1482
|
+
if (value === index) {
|
|
1483
|
+
layer[y][x] = 0;
|
|
1484
|
+
} else if (value > index) {
|
|
1485
|
+
layer[y][x] = value - 1;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
if (this.#color === index) {
|
|
1491
|
+
this.#color = 0;
|
|
1492
|
+
} else if (this.#color > index) {
|
|
1493
|
+
this.#color -= 1;
|
|
1494
|
+
}
|
|
1495
|
+
this.#redraw();
|
|
1496
|
+
this.#handleColorsChange();
|
|
1441
1497
|
}
|
|
1442
1498
|
|
|
1443
1499
|
getColor(index) {
|
|
1444
1500
|
return this.#colors[index];
|
|
1445
1501
|
}
|
|
1446
1502
|
|
|
1447
|
-
setColor(index, r, g, b, a) {
|
|
1503
|
+
setColor(index: number, r: number, g: number, b: number, a: number) {
|
|
1448
1504
|
this.#colors[index] = [r, g, b, a];
|
|
1505
|
+
this.#redraw();
|
|
1506
|
+
this.#handleColorsChange();
|
|
1449
1507
|
}
|
|
1450
1508
|
|
|
1451
|
-
|
|
1509
|
+
/**
|
|
1510
|
+
* Reorder a color within the palette. Index 0 (the background/empty
|
|
1511
|
+
* color) can't be moved, and nothing can move into index 0. Repaints
|
|
1512
|
+
* pixels so they keep showing the same color at its new index.
|
|
1513
|
+
* @param startIndex Current color index
|
|
1514
|
+
* @param endIndex Target color index
|
|
1515
|
+
*/
|
|
1516
|
+
moveColor(startIndex: number, endIndex: number) {
|
|
1517
|
+
if (startIndex === endIndex) { return; }
|
|
1518
|
+
if (startIndex <= 0 || endIndex <= 0
|
|
1519
|
+
|| startIndex >= this.#colors.length || endIndex >= this.#colors.length) {
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const [moved] = this.#colors.splice(startIndex, 1);
|
|
1523
|
+
this.#colors.splice(endIndex, 0, moved);
|
|
1524
|
+
const mapIndex = (value: number) => {
|
|
1525
|
+
if (value === startIndex) { return endIndex; }
|
|
1526
|
+
if (startIndex < endIndex && value > startIndex && value <= endIndex) { return value - 1; }
|
|
1527
|
+
if (startIndex > endIndex && value >= endIndex && value < startIndex) { return value + 1; }
|
|
1528
|
+
return value;
|
|
1529
|
+
};
|
|
1530
|
+
this.#data.forEach((layer) => {
|
|
1531
|
+
for (let y = 0; y < layer.length; y++) {
|
|
1532
|
+
for (let x = 0; x < layer[y].length; x++) {
|
|
1533
|
+
layer[y][x] = mapIndex(layer[y][x]);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
this.#color = mapIndex(this.#color);
|
|
1538
|
+
this.#redraw();
|
|
1539
|
+
this.#handleColorsChange();
|
|
1540
|
+
}
|
|
1452
1541
|
|
|
1542
|
+
#handleColorsChange() {
|
|
1543
|
+
this.dispatchEvent(new CustomEvent('colorschange', {
|
|
1544
|
+
detail: { colors: this.#colors }
|
|
1545
|
+
}));
|
|
1453
1546
|
}
|
|
1454
1547
|
|
|
1455
1548
|
getColorAt(x: number, y: number) {
|
|
1456
1549
|
return this.#export[y][x];
|
|
1457
1550
|
}
|
|
1458
1551
|
|
|
1552
|
+
/**
|
|
1553
|
+
* Instead of returning all colors only include
|
|
1554
|
+
* the currently used colors.
|
|
1555
|
+
*/
|
|
1556
|
+
getActiveColors(): Color[] {
|
|
1557
|
+
// Index 0 is the empty pixel and is never included
|
|
1558
|
+
const used = new Set<number>();
|
|
1559
|
+
for (const layer of this.#data) {
|
|
1560
|
+
getGridColorIndexes(layer).forEach((index) => used.add(index));
|
|
1561
|
+
}
|
|
1562
|
+
return [...used]
|
|
1563
|
+
.sort((a, b) => a - b)
|
|
1564
|
+
.filter((index) => index < this.#colors.length)
|
|
1565
|
+
.map((index) => this.#colors[index]);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1459
1568
|
/**
|
|
1460
1569
|
* Get layer index with first valid color.
|
|
1461
1570
|
* @param x x
|
|
@@ -1484,14 +1593,43 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1484
1593
|
this.#layers.push({
|
|
1485
1594
|
name,
|
|
1486
1595
|
type,
|
|
1487
|
-
|
|
1596
|
+
// `exclude` means "exclude from export"; invert it, don't pass it
|
|
1597
|
+
// straight through, or every layer a caller explicitly includes
|
|
1598
|
+
// (exclude: false) ends up marked excluded.
|
|
1599
|
+
export: !(optional.exclude ?? false),
|
|
1488
1600
|
locked: optional.locked ?? false,
|
|
1489
1601
|
visible: optional.hidden ?? true,
|
|
1490
1602
|
opacity: optional.opacity ?? 1,
|
|
1491
1603
|
});
|
|
1492
|
-
// short hand for initial data
|
|
1604
|
+
// short hand for initial data: `data` is this new layer's own color
|
|
1605
|
+
// group list (getData()'s per-layer shape), applied at its real index
|
|
1606
|
+
// — not routed through setData(), whose indexing is relative to the
|
|
1607
|
+
// layers passed in that call, not this layer's actual position.
|
|
1493
1608
|
if (optional.data) {
|
|
1494
|
-
this.
|
|
1609
|
+
this.#setLayerData(this.#layers.length - 1, optional.data);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
#setLayerData(layerIndex: number, layerColorGroups: { color: number; path: string }[]) {
|
|
1614
|
+
const { type } = this.#layers[layerIndex];
|
|
1615
|
+
switch (type) {
|
|
1616
|
+
case 'pixel':
|
|
1617
|
+
layerColorGroups.forEach(({ color, path }) => {
|
|
1618
|
+
const temp = maskToBitmap(path, this.width, this.height);
|
|
1619
|
+
temp.forEach((tempY, y) => {
|
|
1620
|
+
tempY.forEach((tempX, x) => {
|
|
1621
|
+
if (tempX !== 0) {
|
|
1622
|
+
this.#setPixel(x, y, color, [layerIndex]);
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
});
|
|
1626
|
+
});
|
|
1627
|
+
break;
|
|
1628
|
+
case 'reference':
|
|
1629
|
+
console.log('reference', layerColorGroups);
|
|
1630
|
+
break;
|
|
1631
|
+
default:
|
|
1632
|
+
throw new Error(`unknown type ${type}`);
|
|
1495
1633
|
}
|
|
1496
1634
|
}
|
|
1497
1635
|
|
|
@@ -1606,30 +1744,17 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1606
1744
|
}
|
|
1607
1745
|
|
|
1608
1746
|
/**
|
|
1609
|
-
* Set data
|
|
1747
|
+
* Set data. Mirrors getData()'s shape exactly: `data[layerIndex]` is the
|
|
1748
|
+
* full list of color groups for that layer (one entry per distinct color
|
|
1749
|
+
* used on it), not a single {color, path}. A layer can use more than one
|
|
1750
|
+
* color, and treating `data[i]` as "the i-th layer's one color" instead
|
|
1751
|
+
* of "layer i's color list" misreads every color past the first as
|
|
1752
|
+
* belonging to a different layer — silently painting it onto whichever
|
|
1753
|
+
* other layer happens to sit at that index, or throwing if none does.
|
|
1610
1754
|
*/
|
|
1611
1755
|
setData(data: any[]) {
|
|
1612
|
-
data.forEach((
|
|
1613
|
-
|
|
1614
|
-
switch(type) {
|
|
1615
|
-
case 'pixel':
|
|
1616
|
-
const { color, path } = layerData;
|
|
1617
|
-
const temp = maskToBitmap(path, this.width, this.height);
|
|
1618
|
-
temp.forEach((tempY, y) => {
|
|
1619
|
-
tempY.forEach((tempX, x) => {
|
|
1620
|
-
if (tempX !== 0) {
|
|
1621
|
-
//this.#data[layerIndex][y][x] = color;
|
|
1622
|
-
this.#setPixel(x, y, color, [layerIndex]);
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1625
|
-
});
|
|
1626
|
-
break;
|
|
1627
|
-
case 'reference':
|
|
1628
|
-
console.log('reference', layerData);
|
|
1629
|
-
break;
|
|
1630
|
-
default:
|
|
1631
|
-
throw new Error(`unknown type ${type}`);
|
|
1632
|
-
}
|
|
1756
|
+
data.forEach((layerColorGroups, layerIndex) => {
|
|
1757
|
+
this.#setLayerData(layerIndex, layerColorGroups);
|
|
1633
1758
|
});
|
|
1634
1759
|
}
|
|
1635
1760
|
|
|
@@ -1,39 +0,0 @@
|
|
|
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. |
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<textarea part="input"></textarea>
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { selectComponent, getProps } from '@pictogrammers/element';
|
|
2
|
-
|
|
3
|
-
import './inputTextarea';
|
|
4
|
-
import PgInputText from './inputTextarea';
|
|
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
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
-
|
|
3
|
-
import template from './inputTextarea.html';
|
|
4
|
-
import style from './inputTextarea.css';
|
|
5
|
-
|
|
6
|
-
@Component({
|
|
7
|
-
selector: 'pg-input-textarea',
|
|
8
|
-
style,
|
|
9
|
-
template
|
|
10
|
-
})
|
|
11
|
-
export default class PgInputTextarea 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
|
-
this.$input.addEventListener('focus', this.handleFocus.bind(this));
|
|
23
|
-
this.$input.addEventListener('focus', this.handleBlur.bind(this));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
focus() {
|
|
27
|
-
this.$input.focus();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
skipValue = false;
|
|
31
|
-
|
|
32
|
-
render(changes) {
|
|
33
|
-
if (changes.value && !this.skipValue) {
|
|
34
|
-
this.$input.value = this.value;
|
|
35
|
-
}
|
|
36
|
-
if (changes.placeholder) {
|
|
37
|
-
this.$input.placeholder = this.placeholder;
|
|
38
|
-
}
|
|
39
|
-
if (changes.readOnly) {
|
|
40
|
-
this.$input.readOnly = this.readOnly;
|
|
41
|
-
}
|
|
42
|
-
this.skipValue = false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
handleInput(e) {
|
|
46
|
-
e.stopPropagation();
|
|
47
|
-
this.skipValue = true;
|
|
48
|
-
this.value = e.target.value;
|
|
49
|
-
this.dispatchEvent(
|
|
50
|
-
new CustomEvent('input', {
|
|
51
|
-
detail: {
|
|
52
|
-
value: e.target.value,
|
|
53
|
-
name: e.name
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
handleChange(e) {
|
|
60
|
-
this.dispatchEvent(
|
|
61
|
-
new CustomEvent('change', {
|
|
62
|
-
detail: {
|
|
63
|
-
value: e.target.value,
|
|
64
|
-
name: e.name
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
handleFocus(e: any) {
|
|
71
|
-
e.preventDefault();
|
|
72
|
-
this.dispatchEvent(new CustomEvent('focus'));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
handleBlur(e: any) {
|
|
76
|
-
e.preventDefault();
|
|
77
|
-
this.dispatchEvent(new CustomEvent('blur'));
|
|
78
|
-
}
|
|
79
|
-
}
|