@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pictogrammers/components",
3
3
  "type": "module",
4
- "version": "0.5.25",
4
+ "version": "0.5.27",
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);
@@ -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 + 5, y * totalSize + 5, 3, 0, 2 * Math.PI);
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 + 5, y * totalSize + 5, 2, 0, 2 * Math.PI);
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.#colors.length > 2) {
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
- 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; }
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
- 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
+ }
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
- 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),
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.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}`);
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((layerData, layerIndex) => {
1612
- const { type } = this.#layers[layerIndex];
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
- // Overlay
81
- const observer = new IntersectionObserver((entries) => {
82
- entries.forEach(entry => {
83
- console.log(entry.intersectionRatio);
84
- //this.$overlay.classList.toggle('no-offset', entry.intersectionRatio < 1);
85
- // entry.isIntersecting will be false if it is entirely outside the viewport
86
- if (!entry.isIntersecting) {
87
- console.log('Element is completely outside the viewport');
88
- } else {
89
- console.log('Element is at least partially inside the viewport');
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
- }