@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pictogrammers/components",
3
3
  "type": "module",
4
- "version": "0.5.24",
4
+ "version": "0.5.26",
5
5
  "license": "MIT",
6
6
  "author": "Austin Andrews",
7
7
  "scripts": {
@@ -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
- name: 'Layer 1',
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
- * Clip a cached grid so its dimensions never exceed the current
309
- * width/height. Used when the canvas is made smaller.
310
- * @param grid 2d grid to clip in place
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
- #clipGrid(grid: number[][]) {
313
- if (grid.length > this.height) {
314
- grid.length = this.height;
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 < grid.length; y++) {
317
- if (grid[y].length > this.width) {
318
- grid[y].length = this.width;
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
- // When the canvas shrinks, clip the cached grids so stale rows/columns
325
- // outside the new bounds are dropped (the grow case is handled below).
326
- this.#clipGrid(this.#export);
327
- this.#clipGrid(this.#selection);
328
- this.#clipGrid(this.#selectionPreview);
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 clipped #selection grid.
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
- const data = this.#data.toReversed();
342
- const layerCount = data.length;
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
- if (x >= data[0][y].length) {
354
- for (let l = 0; l < layerCount; l++) {
355
- data[l][y].push(0);
356
- this.#export[y].push(0);
357
- this.#selection[y].push(0);
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 + 5, y * totalSize + 5, 3, 0, 2 * Math.PI);
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 + 5, y * totalSize + 5, 2, 0, 2 * Math.PI);
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.#colors.length > 2) {
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
- removeColor(index) {
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
- moveColor(startIndex, endIndex) {
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
- export: optional.exclude ?? true,
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.setData(optional.data);
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((layerData, layerIndex) => {
1613
- const { type } = this.#layers[layerIndex];
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
- }