@pictogrammers/components 0.5.19 → 0.5.21

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.19",
4
+ "version": "0.5.21",
5
5
  "license": "MIT",
6
6
  "author": "Austin Andrews",
7
7
  "scripts": {
@@ -9,7 +9,7 @@ import '@pictogrammers/components/pgHeader.js';
9
9
  ```html
10
10
  <pg-header
11
11
  logo="...svg path..."
12
- name="Material Design Icons"></pg-nav>
12
+ name="Material Design Icons"></pg-header>
13
13
  ```
14
14
 
15
15
  | Slots | Tested | Description |
@@ -4,5 +4,5 @@
4
4
  name="Material Design Icons">
5
5
  <pg-search slot="search"></pg-search>
6
6
  <pg-nav slot="nav"></pg-nav>
7
- </pg-nav>
7
+ </pg-header>
8
8
  </div>
@@ -1,11 +1,21 @@
1
1
  import { Component, Part, Prop } from '@pictogrammers/element';
2
2
  import PgModal from '../../modal';
3
3
 
4
+ import '../../../modalHeader/modalHeader';
5
+ import '../../../modalBody/modalBody';
6
+ import '../../../modalFooter/modalFooter';
7
+
4
8
  import template from './basic.html';
5
9
 
6
10
  @Component({
7
11
  selector: 'x-my-modal',
8
- template: `<main>Hello! <button part="close">Close</button></main>`
12
+ template: `
13
+ <pg-modal-header>My Modal</pg-modal-header>
14
+ <pg-modal-body>Hello!</pg-modal-body>
15
+ <pg-modal-footer>
16
+ <button part="close">Close</button>
17
+ </pg-modal-footer>
18
+ `
9
19
  })
10
20
  export class XMyModal extends PgModal {
11
21
 
@@ -0,0 +1,17 @@
1
+ # `<pg-modal-body>`
2
+
3
+ The `pg-modal-body` component is a presentational component for the body section of a `PgModal`.
4
+
5
+ ```typescript
6
+ import '@pictogrammers/components/pg/modalBody';
7
+ ```
8
+
9
+ ```html
10
+ <pg-modal-body>Content</pg-modal-body>
11
+ ```
12
+
13
+ ## CSS Variables
14
+
15
+ | CSS Variables | Default | Description |
16
+ | ------------------------- | --------------- | ----------- |
17
+ | `--pg-modal-body-padding` | `0.5rem 1rem` | Padding |
@@ -0,0 +1,3 @@
1
+ import PgModalBody from './modalBody';
2
+
3
+ export default PgModalBody;
@@ -0,0 +1,6 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+ [part="body"] {
5
+ padding: var(--pg-modal-body-padding, 0.5rem 1rem);
6
+ }
@@ -0,0 +1,3 @@
1
+ <main part="body">
2
+ <slot></slot>
3
+ </main>
@@ -0,0 +1,13 @@
1
+ import { Component } from '@pictogrammers/element';
2
+
3
+ import template from './modalBody.html';
4
+ import style from './modalBody.css';
5
+
6
+ @Component({
7
+ selector: 'pg-modal-body',
8
+ style,
9
+ template
10
+ })
11
+ export default class PgModalBody extends HTMLElement {
12
+
13
+ }
@@ -0,0 +1,23 @@
1
+ # `<pg-modal-footer>`
2
+
3
+ The `pg-modal-footer` component is a presentational component for the footer section of a `PgModal`.
4
+
5
+ ```typescript
6
+ import '@pictogrammers/components/pg/modalFooter';
7
+ ```
8
+
9
+ ```html
10
+ <pg-modal-footer>
11
+ <button>Close</button>
12
+ </pg-modal-footer>
13
+ ```
14
+
15
+ ## CSS Variables
16
+
17
+ | CSS Variables | Default | Description |
18
+ | ------------------------------------- | --------------- | --------------- |
19
+ | `--pg-modal-footer-background` | `#f1f1f1` | Background |
20
+ | `--pg-modal-footer-border-color` | `#ccc` | Border Color |
21
+ | `--pg-modal-footer-padding` | `0.75rem 1rem` | Padding |
22
+ | `--pg-modal-footer-justify-content` | `flex-end` | Justify Content |
23
+ | `--pg-modal-footer-gap` | `0.5rem` | Gap Between Items |
@@ -0,0 +1,3 @@
1
+ import PgModalFooter from './modalFooter';
2
+
3
+ export default PgModalFooter;
@@ -0,0 +1,12 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+ [part="footer"] {
5
+ display: flex;
6
+ flex-direction: row;
7
+ padding: var(--pg-modal-footer-padding, 0.75rem 1rem);
8
+ border-top: 1px solid var(--pg-modal-footer-border-color, #ccc);
9
+ background: var(--pg-modal-footer-background, #f1f1f1);
10
+ justify-content: var(--pg-modal-footer-justify-content, flex-end);
11
+ gap: var(--pg-modal-footer-gap, 0.5rem);
12
+ }
@@ -0,0 +1,3 @@
1
+ <footer part="footer">
2
+ <slot></slot>
3
+ </footer>
@@ -0,0 +1,13 @@
1
+ import { Component } from '@pictogrammers/element';
2
+
3
+ import template from './modalFooter.html';
4
+ import style from './modalFooter.css';
5
+
6
+ @Component({
7
+ selector: 'pg-modal-footer',
8
+ style,
9
+ template
10
+ })
11
+ export default class PgModalFooter extends HTMLElement {
12
+
13
+ }
@@ -0,0 +1,19 @@
1
+ # `<pg-modal-header>`
2
+
3
+ The `pg-modal-header` component is a presentational component for the header section of a `PgModal`.
4
+
5
+ ```typescript
6
+ import '@pictogrammers/components/pg/modalHeader';
7
+ ```
8
+
9
+ ```html
10
+ <pg-modal-header>Title</pg-modal-header>
11
+ ```
12
+
13
+ ## CSS Variables
14
+
15
+ | CSS Variables | Default | Description |
16
+ | ---------------------------------- | ----------- | ------------ |
17
+ | `--pg-modal-header-background` | `#f1f1f1` | Background |
18
+ | `--pg-modal-header-border-color` | `#ccc` | Border Color |
19
+ | `--pg-modal-header-padding` | `0.75rem 1rem` | Padding |
@@ -0,0 +1,3 @@
1
+ import PgModalHeader from './modalHeader';
2
+
3
+ export default PgModalHeader;
@@ -0,0 +1,9 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+ [part="header"] {
5
+ border-bottom: 1px solid var(--pg-modal-header-border-color, #ccc);
6
+ background: var(--pg-modal-header-background, #f1f1f1);
7
+ padding: var(--pg-modal-header-padding, 0.75rem 1rem);
8
+ font-size: 1.25rem;
9
+ }
@@ -0,0 +1,3 @@
1
+ <header part="header">
2
+ <slot></slot>
3
+ </header>
@@ -0,0 +1,13 @@
1
+ import { Component } from '@pictogrammers/element';
2
+
3
+ import template from './modalHeader.html';
4
+ import style from './modalHeader.css';
5
+
6
+ @Component({
7
+ selector: 'pg-modal-header',
8
+ style,
9
+ template
10
+ })
11
+ export default class PgModalHeader extends HTMLElement {
12
+
13
+ }
@@ -4,5 +4,5 @@
4
4
  <button part="single">Single Item</button>
5
5
  <button part="editToggle">Toggle Edit Mode</button>
6
6
  </div>
7
- <pg-modification part="modification"></pg-nav>
7
+ <pg-modification part="modification"></pg-modification>
8
8
  </div>
package/pg/tree/README.md CHANGED
@@ -1,81 +1,122 @@
1
- # Tree
2
-
3
- The `pg-tree` is used to render a tree list of items.
4
-
5
- - Folder / Item symbols
6
- - Drag and Drop
7
- - Context Menu
8
-
9
- ## Usage
10
-
11
- While setup for a normal file tree this can be used for any folder structure.
12
-
13
- ```typescript
14
- import PgTree, { SelectedTreeItem } from '@pictogrammers/components/pg/tree';
15
- ```
16
-
17
- ## Events
18
-
19
- - select
20
- - rename
21
- - menu
22
- - action
23
- - drop
24
-
25
- ```typescript
26
- this.$tree.addEventListener('select', (e: any) => {
27
- const { items } = e.detail;
28
- // items is a
29
- this.selectedItems = items;
30
- });
31
- ```
32
-
33
- ```typescript
34
- this.$tree.addEventListener('rename', (e: any) => {
35
- const { item, label } = e.detail;
36
- const itemData = item.getData();
37
- itemData.label = label;
38
- });
39
- ```
40
-
41
- ```typescript
42
- this.$tree.addEventListener('menu', (e: any) => {
43
- // menu item click
44
- });
45
- ```
46
-
47
- ```typescript
48
- this.$tree.addEventListener('action', (e: any) => {
49
- const { item, actionIndex } = e.detail;
50
- // item is a wrapper with all utility functions
51
- switch(actionIndex) {
52
- case 0:
53
- // select in view
54
- break;
55
- case 1:
56
- item.remove();
57
- break;
58
- }
59
- });
60
- ```
61
-
62
- ```typescript
63
- this.$tree.addEventListener('drop', (e: any) => {
64
- // cancelable
65
- const { items, dropIndexes } = e.detail;
66
- // If folder ignore items under folder
67
-
68
- items.forEach(() => {
69
- item.move(dropIndexes);
70
- })
71
- });
72
- ```
73
-
74
- ## Custom Size
75
-
76
- Resize from the default 32px height all the way down to 24px tall via a CSS Variable.
77
-
78
- ```css
79
- --pg-icon-width: 1rem;
80
- --pg-icon-height: 1rem;
81
- ```
1
+ # Tree
2
+
3
+ The `pg-tree` is used to render a tree list of items.
4
+
5
+ - Folder / Item symbols
6
+ - Drag and Drop
7
+ - Context Menu
8
+
9
+ ## Usage
10
+
11
+ While setup for a normal file tree this can be used for any folder structure.
12
+
13
+ ```typescript
14
+ import PgTree, { SelectedTreeItem } from '@pictogrammers/components/pg/tree';
15
+ ```
16
+
17
+ ## Item Data Shape
18
+
19
+ Each item in the `items` array follows this structure:
20
+
21
+ ```typescript
22
+ {
23
+ label: string;
24
+ icon: { path: string };
25
+ isFolder: boolean; // true enables the drop-onto zone and .items CSS class
26
+ expanded?: boolean; // folders only
27
+ items?: any[]; // child items (folders only)
28
+ actions?: {
29
+ type: typeof PgTreeButtonIcon;
30
+ icon: string;
31
+ enabled: boolean;
32
+ label?: string; // used as button title / aria-label
33
+ }[];
34
+ }
35
+ ```
36
+
37
+ ## Events
38
+
39
+ - `select` — one or more items selected / deselected
40
+ - `rename` — item label edited inline
41
+ - `menu` — right-click context menu requested
42
+ - `action` an action button clicked
43
+ - `move` drag-and-drop reorder completed
44
+
45
+ ```typescript
46
+ this.$tree.addEventListener('select', (e: any) => {
47
+ const { items } = e.detail;
48
+ this.selectedItems = items;
49
+ });
50
+ ```
51
+
52
+ ```typescript
53
+ this.$tree.addEventListener('rename', (e: any) => {
54
+ const { item, label } = e.detail;
55
+ item.getData().label = label;
56
+ });
57
+ ```
58
+
59
+ ```typescript
60
+ this.$tree.addEventListener('menu', (e: any) => {
61
+ const { item, x, y } = e.detail;
62
+ // show context menu at (x, y) for item
63
+ });
64
+ ```
65
+
66
+ ```typescript
67
+ this.$tree.addEventListener('action', (e: any) => {
68
+ const { item, actionIndex } = e.detail;
69
+ switch (actionIndex) {
70
+ case 0:
71
+ // toggle visibility
72
+ break;
73
+ case 1:
74
+ item.remove();
75
+ break;
76
+ }
77
+ });
78
+ ```
79
+
80
+ ```typescript
81
+ this.$tree.addEventListener('move', (e: any) => {
82
+ const { item, position } = e.detail;
83
+ // position is 'before' | 'after' | 'on'
84
+ this.selectedItems.forEach((selected: SelectedTreeItem) => {
85
+ selected.move(item, position);
86
+ });
87
+ });
88
+ ```
89
+
90
+ ## SelectedTreeItem API
91
+
92
+ The `item` object passed in event details wraps the raw data with helper methods:
93
+
94
+ ```typescript
95
+ item.indexes // number[] — path to this item in the tree
96
+ item.getData() // returns the raw data object
97
+ item.getParentData() // returns the parent's raw data (or the tree root)
98
+ item.remove() // removes the item and clears selection
99
+ item.move(targetItem, position) // moves to 'before' | 'after' | 'on' the target
100
+ ```
101
+
102
+ ## Custom Size
103
+
104
+ Resize the row height via a CSS variable (default `2`, range `1`–`2`):
105
+
106
+ ```css
107
+ pg-tree {
108
+ --pg-tree-size: 1.5;
109
+ }
110
+ ```
111
+
112
+ ## CSS Custom Properties
113
+
114
+ | Property | Description |
115
+ |---|---|
116
+ | `--pg-tree-background` | Background of the tree container |
117
+ | `--pg-tree-item-background` | Default item row background |
118
+ | `--pg-tree-item-color` | Icon and text color |
119
+ | `--pg-tree-item-font-size` | Label font size |
120
+ | `--pg-tree-selected-primary` | Selection indicator / toggle color |
121
+ | `--pg-tree-selected-secondary` | Selected row background |
122
+ | `--pg-tree-selected-secondary-hover` | Selected row hover background |
package/pg/tree/tree.ts CHANGED
@@ -49,11 +49,7 @@ export default class PgTree extends HTMLElement {
49
49
  this.dispatchEvent(new CustomEvent('move', {
50
50
  detail: { position: e.detail.position }
51
51
  }));
52
- this.dispatchEvent(new CustomEvent('select', {
53
- detail: {
54
- items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
55
- }
56
- }));
52
+ this.#dispatchSelect();
57
53
  });
58
54
  this.$items.addEventListener('rename', (e: any) => {
59
55
  e.stopPropagation();
@@ -114,11 +110,7 @@ export default class PgTree extends HTMLElement {
114
110
  }
115
111
  }
116
112
 
117
- this.dispatchEvent(new CustomEvent('select', {
118
- detail: {
119
- items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
120
- }
121
- }));
113
+ this.#dispatchSelect();
122
114
  });
123
115
  this.$items.addEventListener('up', (e: any) => {
124
116
  e.stopPropagation();
@@ -141,9 +133,7 @@ export default class PgTree extends HTMLElement {
141
133
  this.#selectedIndexes.forEach((idxs: any) => this.#getItem(idxs).selected = false);
142
134
  this.#selectedIndexes.clear();
143
135
  this.#lastSelectedIndexes = null;
144
- this.dispatchEvent(new CustomEvent('select', {
145
- detail: { items: [] }
146
- }));
136
+ this.#dispatchSelect();
147
137
  } else if (e.key === 'Delete') {
148
138
  const toRemove = [...this.#selectedIndexes.values()] as number[][];
149
139
  // Remove higher sibling indexes first to avoid index shifts
@@ -158,6 +148,7 @@ export default class PgTree extends HTMLElement {
158
148
  toRemove.forEach((indexes: number[]) => this.#removeItem(indexes));
159
149
  this.#selectedIndexes.clear();
160
150
  this.#lastSelectedIndexes = null;
151
+ this.#dispatchSelect();
161
152
  }
162
153
  });
163
154
  this.$items.addEventListener('itemdragstart', (e: any) => {
@@ -186,12 +177,7 @@ export default class PgTree extends HTMLElement {
186
177
  }
187
178
  });
188
179
  this.$items.classList.toggle('dragging', true);
189
- // Sync selection to consumers so #selectedItems reflects the dragged item
190
- this.dispatchEvent(new CustomEvent('select', {
191
- detail: {
192
- items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
193
- }
194
- }));
180
+ this.#dispatchSelect();
195
181
  });
196
182
  this.$items.addEventListener('itemdragend', () => {
197
183
  this.#draggingElements.forEach(el => el.classList.remove('dragging'));
@@ -351,6 +337,7 @@ export default class PgTree extends HTMLElement {
351
337
  this.#removeItem(indexes);
352
338
  this.#selectedIndexes.clear();
353
339
  this.#lastSelectedIndexes = null;
340
+ this.#dispatchSelect();
354
341
  },
355
342
  getData: () => this.#getItem(indexes),
356
343
  getParentData: () => {
@@ -359,6 +346,7 @@ export default class PgTree extends HTMLElement {
359
346
  },
360
347
  move: (item: SelectedTreeItem, position: string) => {
361
348
  this.#batchMove([indexes], item.indexes, position);
349
+ this.#dispatchSelect();
362
350
  }
363
351
  };
364
352
  }
@@ -368,11 +356,21 @@ export default class PgTree extends HTMLElement {
368
356
  const unproxyItem = getProxyValue(item);
369
357
  item.selected = false;
370
358
  this.#selectedIndexes.delete(unproxyItem);
359
+ this.#dispatchSelect();
371
360
  }
372
361
 
373
362
  unselectAll() {
374
363
  this.#selectedIndexes.forEach((idxs: any) => this.#getItem(idxs).selected = false);
375
364
  this.#selectedIndexes.clear();
365
+ this.#dispatchSelect();
366
+ }
367
+
368
+ #dispatchSelect() {
369
+ this.dispatchEvent(new CustomEvent('select', {
370
+ detail: {
371
+ items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
372
+ }
373
+ }));
376
374
  }
377
375
 
378
376
  #calculateDragExcludes(): string[] {
@@ -11,8 +11,13 @@
11
11
  border-radius: 0.125rem;
12
12
  }
13
13
 
14
- [part="button"]:hover {
14
+ [part="button"]:hover:not(:disabled) {
15
15
  box-shadow: 0 0 0.125rem rgba(0, 0, 0, 0.25);
16
16
  background-color: rgba(255, 255, 255, 0.5);
17
17
  border-radius: 0.125rem;
18
+ }
19
+
20
+ [part="button"]:disabled {
21
+ opacity: 0.35;
22
+ cursor: default;
18
23
  }
@@ -1,42 +1,52 @@
1
- import { Component, Prop, Part } from '@pictogrammers/element';
2
-
3
- import template from './treeButtonIcon.html';
4
- import style from './treeButtonIcon.css';
5
- import PgIcon from '../icon/icon';
6
-
7
- const noIcon = 'M0 0h24v24H0V0zm2 2v20h20V2H2z';
8
-
9
- @Component({
10
- selector: 'pg-tree-button-icon',
11
- style,
12
- template
13
- })
14
- export default class PgTreeButtonIcon extends HTMLElement {
15
-
16
- @Prop() index: number;
17
- @Prop() icon: string = noIcon;
18
-
19
- @Part() $button: HTMLButtonElement;
20
- @Part() $icon: PgIcon;
21
-
22
- connectedCallback() {
23
- this.$button.addEventListener('click', this.#handleClick.bind(this))
24
- }
25
-
26
- #handleClick(e) {
27
- this.dispatchEvent(new CustomEvent('action', {
28
- bubbles: true,
29
- composed: true,
30
- detail: {
31
- index: this.index
32
- }
33
- }));
34
- }
35
-
36
- render(changes) {
37
- if (changes.icon) {
38
- this.$icon.path = this.icon;
39
- }
40
- }
41
-
42
- }
1
+ import { Component, Prop, Part } from '@pictogrammers/element';
2
+
3
+ import template from './treeButtonIcon.html';
4
+ import style from './treeButtonIcon.css';
5
+ import PgIcon from '../icon/icon';
6
+
7
+ const noIcon = 'M0 0h24v24H0V0zm2 2v20h20V2H2z';
8
+
9
+ @Component({
10
+ selector: 'pg-tree-button-icon',
11
+ style,
12
+ template
13
+ })
14
+ export default class PgTreeButtonIcon extends HTMLElement {
15
+
16
+ @Prop() index: number;
17
+ @Prop() icon: string = noIcon;
18
+ @Prop() enabled: boolean = false;
19
+ @Prop() label: string = '';
20
+
21
+ @Part() $button: HTMLButtonElement;
22
+ @Part() $icon: PgIcon;
23
+
24
+ connectedCallback() {
25
+ this.$button.addEventListener('click', this.#handleClick.bind(this));
26
+ }
27
+
28
+ #handleClick() {
29
+ if (!this.enabled) return;
30
+ this.dispatchEvent(new CustomEvent('action', {
31
+ bubbles: true,
32
+ composed: true,
33
+ detail: {
34
+ index: this.index
35
+ }
36
+ }));
37
+ }
38
+
39
+ render(changes) {
40
+ if (changes.icon) {
41
+ this.$icon.path = this.icon;
42
+ }
43
+ if (changes.enabled) {
44
+ this.$button.disabled = !this.enabled;
45
+ }
46
+ if (changes.label) {
47
+ this.$button.title = this.label;
48
+ this.$button.setAttribute('aria-label', this.label);
49
+ }
50
+ }
51
+
52
+ }