@pictogrammers/components 0.5.25 → 0.5.27
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 +168 -42
- package/pg/overlayMenu/overlayMenu.ts +2 -0
- package/pg/overlaySelectMenu/overlaySelectMenu.ts +20 -12
- 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);
|
|
@@ -354,15 +351,26 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
354
351
|
this.$selectionPath.classList.toggle('hide', true);
|
|
355
352
|
}
|
|
356
353
|
|
|
357
|
-
// Render individual pixels, topmost non-transparent layer wins
|
|
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.
|
|
358
361
|
for (let y = 0; y < this.height; y++) {
|
|
359
362
|
for (let x = 0; x < this.width; x++) {
|
|
363
|
+
let painted = false;
|
|
360
364
|
for (let l = this.#data.length - 1; l >= 0; l--) {
|
|
361
365
|
if (this.#data[l][y][x] !== 0) {
|
|
362
366
|
this.#setPixel(x, y, this.#data[l][y][x], [l]);
|
|
367
|
+
painted = true;
|
|
363
368
|
break;
|
|
364
369
|
}
|
|
365
370
|
}
|
|
371
|
+
if (!painted && this.#export[y][x] !== 0) {
|
|
372
|
+
this.#setPixel(x, y, 0, [this.#layer[0]]);
|
|
373
|
+
}
|
|
366
374
|
}
|
|
367
375
|
}
|
|
368
376
|
}
|
|
@@ -547,15 +555,18 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
547
555
|
);
|
|
548
556
|
// preview layer
|
|
549
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);
|
|
550
561
|
pixels.forEach(({ x, y }) => {
|
|
551
562
|
this.#previewLayerContext.fillStyle = WHITE;
|
|
552
563
|
this.#previewLayerContext.beginPath();
|
|
553
|
-
this.#previewLayerContext.arc(x * totalSize +
|
|
564
|
+
this.#previewLayerContext.arc(x * totalSize + center, y * totalSize + center, outerRadius, 0, 2 * Math.PI);
|
|
554
565
|
this.#previewLayerContext.closePath();
|
|
555
566
|
this.#previewLayerContext.fill();
|
|
556
567
|
this.#previewLayerContext.fillStyle = '#1B79C8';
|
|
557
568
|
this.#previewLayerContext.beginPath();
|
|
558
|
-
this.#previewLayerContext.arc(x * totalSize +
|
|
569
|
+
this.#previewLayerContext.arc(x * totalSize + center, y * totalSize + center, innerRadius, 0, 2 * Math.PI);
|
|
559
570
|
this.#previewLayerContext.closePath();
|
|
560
571
|
this.#previewLayerContext.fill();
|
|
561
572
|
});
|
|
@@ -1223,7 +1234,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1223
1234
|
|
|
1224
1235
|
invert() {
|
|
1225
1236
|
// Only works with 2 colors
|
|
1226
|
-
if (this
|
|
1237
|
+
if (this.getActiveColors().length > 2) {
|
|
1227
1238
|
return;
|
|
1228
1239
|
}
|
|
1229
1240
|
iterateGrid(this.#data[this.#layer[0]], (x, y) => {
|
|
@@ -1431,30 +1442,129 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1431
1442
|
return this.#colors;
|
|
1432
1443
|
}
|
|
1433
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
|
+
|
|
1434
1463
|
addColor(r: number, g: number, b: number, a: number) {
|
|
1435
1464
|
this.#colors.push([r, g, b, a]);
|
|
1465
|
+
this.#handleColorsChange();
|
|
1436
1466
|
}
|
|
1437
1467
|
|
|
1438
|
-
|
|
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; }
|
|
1439
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();
|
|
1440
1497
|
}
|
|
1441
1498
|
|
|
1442
1499
|
getColor(index) {
|
|
1443
1500
|
return this.#colors[index];
|
|
1444
1501
|
}
|
|
1445
1502
|
|
|
1446
|
-
setColor(index, r, g, b, a) {
|
|
1503
|
+
setColor(index: number, r: number, g: number, b: number, a: number) {
|
|
1447
1504
|
this.#colors[index] = [r, g, b, a];
|
|
1505
|
+
this.#redraw();
|
|
1506
|
+
this.#handleColorsChange();
|
|
1448
1507
|
}
|
|
1449
1508
|
|
|
1450
|
-
|
|
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
|
+
}
|
|
1451
1541
|
|
|
1542
|
+
#handleColorsChange() {
|
|
1543
|
+
this.dispatchEvent(new CustomEvent('colorschange', {
|
|
1544
|
+
detail: { colors: this.#colors }
|
|
1545
|
+
}));
|
|
1452
1546
|
}
|
|
1453
1547
|
|
|
1454
1548
|
getColorAt(x: number, y: number) {
|
|
1455
1549
|
return this.#export[y][x];
|
|
1456
1550
|
}
|
|
1457
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
|
+
|
|
1458
1568
|
/**
|
|
1459
1569
|
* Get layer index with first valid color.
|
|
1460
1570
|
* @param x x
|
|
@@ -1483,14 +1593,43 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1483
1593
|
this.#layers.push({
|
|
1484
1594
|
name,
|
|
1485
1595
|
type,
|
|
1486
|
-
|
|
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),
|
|
1487
1600
|
locked: optional.locked ?? false,
|
|
1488
1601
|
visible: optional.hidden ?? true,
|
|
1489
1602
|
opacity: optional.opacity ?? 1,
|
|
1490
1603
|
});
|
|
1491
|
-
// 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.
|
|
1492
1608
|
if (optional.data) {
|
|
1493
|
-
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}`);
|
|
1494
1633
|
}
|
|
1495
1634
|
}
|
|
1496
1635
|
|
|
@@ -1605,30 +1744,17 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1605
1744
|
}
|
|
1606
1745
|
|
|
1607
1746
|
/**
|
|
1608
|
-
* 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.
|
|
1609
1754
|
*/
|
|
1610
1755
|
setData(data: any[]) {
|
|
1611
|
-
data.forEach((
|
|
1612
|
-
|
|
1613
|
-
switch(type) {
|
|
1614
|
-
case 'pixel':
|
|
1615
|
-
const { color, path } = layerData;
|
|
1616
|
-
const temp = maskToBitmap(path, this.width, this.height);
|
|
1617
|
-
temp.forEach((tempY, y) => {
|
|
1618
|
-
tempY.forEach((tempX, x) => {
|
|
1619
|
-
if (tempX !== 0) {
|
|
1620
|
-
//this.#data[layerIndex][y][x] = color;
|
|
1621
|
-
this.#setPixel(x, y, color, [layerIndex]);
|
|
1622
|
-
}
|
|
1623
|
-
});
|
|
1624
|
-
});
|
|
1625
|
-
break;
|
|
1626
|
-
case 'reference':
|
|
1627
|
-
console.log('reference', layerData);
|
|
1628
|
-
break;
|
|
1629
|
-
default:
|
|
1630
|
-
throw new Error(`unknown type ${type}`);
|
|
1631
|
-
}
|
|
1756
|
+
data.forEach((layerColorGroups, layerIndex) => {
|
|
1757
|
+
this.#setLayerData(layerIndex, layerColorGroups);
|
|
1632
1758
|
});
|
|
1633
1759
|
}
|
|
1634
1760
|
|
|
@@ -63,7 +63,9 @@ export default class PgOverlayMenu extends PgOverlay {
|
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
65
|
}, {
|
|
66
|
+
// @ts-ignore
|
|
66
67
|
trackVisibility: true,
|
|
68
|
+
delay: 100,
|
|
67
69
|
root: null, // Defaults to the browser viewport
|
|
68
70
|
threshold: 0 // Triggers as soon as even 1 pixel enters or leaves
|
|
69
71
|
});
|
|
@@ -77,18 +77,26 @@ export default class PgOverlaySelectMenu extends PgOverlay {
|
|
|
77
77
|
this.$overlay.style.setProperty('--pg-overlay-menu-_y', `${y}px`);
|
|
78
78
|
// Focus
|
|
79
79
|
this.$menu.focus(index);
|
|
80
|
-
//
|
|
81
|
-
const observer = new IntersectionObserver((
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
80
|
+
// Keep the menu fully within the viewport once its real size is known
|
|
81
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
82
|
+
observer.disconnect();
|
|
83
|
+
if (entry.intersectionRatio === 1) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const { top, bottom } = entry.boundingClientRect;
|
|
87
|
+
const viewportHeight = entry.rootBounds?.height ?? window.innerHeight;
|
|
88
|
+
let offset = 0;
|
|
89
|
+
if (bottom > viewportHeight) {
|
|
90
|
+
offset = viewportHeight - bottom;
|
|
91
|
+
}
|
|
92
|
+
// Prefer keeping the top edge visible over the bottom if it can't fully fit
|
|
93
|
+
if (top + offset < 0) {
|
|
94
|
+
offset = -top;
|
|
95
|
+
}
|
|
96
|
+
if (offset !== 0) {
|
|
97
|
+
y += offset;
|
|
98
|
+
this.$overlay.style.setProperty('--pg-overlay-menu-_y', `${y}px`);
|
|
99
|
+
}
|
|
92
100
|
}, {
|
|
93
101
|
root: null, // Defaults to the browser viewport
|
|
94
102
|
threshold: 1 // Triggers as soon as even 1 pixel enters or leaves
|
|
@@ -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
|
-
}
|