@operato/data-tree 7.1.1 → 7.1.3

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.
@@ -0,0 +1,260 @@
1
+ import { html, css, LitElement, TemplateResult, nothing } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import { ScrollbarStyles } from '@operato/styles'
4
+
5
+ type TreeItem = {
6
+ [key: string]: any
7
+ __status__?: { [state: string]: boolean | string }
8
+ }
9
+
10
+ @customElement('ox-tree-horizontal')
11
+ export class TreeViewHorizontal extends LitElement {
12
+ static styles = [
13
+ ScrollbarStyles,
14
+ css`
15
+ :host {
16
+ display: block;
17
+ overflow: auto;
18
+ padding: 1em;
19
+ }
20
+
21
+ ul,
22
+ li {
23
+ list-style: none;
24
+ margin: 0;
25
+ padding: 0;
26
+ position: relative;
27
+ }
28
+
29
+ ul[root] {
30
+ margin: 0 0 1em;
31
+ text-align: left;
32
+ }
33
+
34
+ ul[root]:before {
35
+ display: none;
36
+ }
37
+
38
+ ul {
39
+ display: flex;
40
+ flex-direction: column;
41
+ align-items: flex-start;
42
+ justify-content: center;
43
+ }
44
+
45
+ ul {
46
+ width: 100%;
47
+ }
48
+
49
+ li {
50
+ display: flex;
51
+ flex-direction: row;
52
+ position: relative;
53
+ align-items: center;
54
+ }
55
+
56
+ li:before,
57
+ li:after {
58
+ content: '';
59
+ position: absolute;
60
+ left: -1em;
61
+ border-left: 1px solid #666;
62
+ }
63
+
64
+ li:before {
65
+ top: 0;
66
+ bottom: 50%;
67
+ }
68
+
69
+ li:after {
70
+ top: 50%;
71
+ bottom: 0;
72
+ border-top: 1px solid #666;
73
+ width: 1em;
74
+ }
75
+
76
+ ul[root] > li:after {
77
+ content: none;
78
+ }
79
+
80
+ li:first-child:before {
81
+ top: 50%;
82
+ }
83
+
84
+ li:last-child:after {
85
+ bottom: 50%;
86
+ }
87
+
88
+ li > ul {
89
+ padding-left: 2em;
90
+ position: relative;
91
+ }
92
+
93
+ li > ul:before {
94
+ content: '';
95
+ position: absolute;
96
+ top: 0;
97
+ bottom: -2px;
98
+ left: 0;
99
+ border-bottom: 1px solid #666;
100
+ }
101
+
102
+ code,
103
+ span {
104
+ border: solid 0.1em #666;
105
+ border-radius: 0.2em;
106
+ display: inline-block;
107
+ margin: 0.3em 0;
108
+ position: relative;
109
+ min-width: 120px;
110
+ background-color: #f9f9f9;
111
+ }
112
+
113
+ span[selected] {
114
+ color: var(--md-sys-color-on-tertiary);
115
+ background-color: var(--md-sys-color-tertiary);
116
+ }
117
+
118
+ ul:before,
119
+ code:before,
120
+ span:before {
121
+ content: '';
122
+ width: 1em;
123
+ top: 50%;
124
+ position: absolute;
125
+ transform: translateY(-50%);
126
+ }
127
+
128
+ ul:before {
129
+ left: -1em;
130
+ }
131
+
132
+ code:before,
133
+ span:before {
134
+ left: -1.1em;
135
+ }
136
+
137
+ ul[root] > li {
138
+ margin-left: 0;
139
+ }
140
+
141
+ ul[root] > li:before,
142
+ ul[root] > li:after,
143
+ ul[root] > li > code:before,
144
+ ul[root] > li > span:before {
145
+ outline: none;
146
+ }
147
+
148
+ span {
149
+ display: inline-block;
150
+ background-color: var(--md-sys-color-surface);
151
+
152
+ div[header] {
153
+ font-weight: bold;
154
+ font-size: 0.8em;
155
+ color: var(--md-sys-color-on-primary, #fff);
156
+ background-color: var(--md-sys-color-primary, #333);
157
+ padding: 0.5em 1em;
158
+ text-align: center;
159
+ width: 100%;
160
+ box-sizing: border-box;
161
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
162
+ }
163
+
164
+ div[label] {
165
+ font-size: 1em;
166
+ background-color: transparent;
167
+ padding: 0.5em;
168
+ text-align: left;
169
+ }
170
+
171
+ div[description] {
172
+ font-size: 0.8em;
173
+ background-color: transparent;
174
+ padding: 0.5em;
175
+ text-align: left;
176
+ }
177
+ }
178
+ `
179
+ ]
180
+
181
+ @property({ type: Object }) data?: TreeItem | TreeItem[]
182
+ @property({ type: Object }) selected?: TreeItem
183
+ @property({ type: String, attribute: 'id-property' }) idProperty: string = 'id'
184
+ @property({ type: String, attribute: 'label-property' }) labelProperty: string = 'label'
185
+ @property({ type: String, attribute: 'description-property' }) descriptionProperty?: string
186
+ @property({ type: String, attribute: 'children-property' }) childrenProperty: string = 'children'
187
+
188
+ render() {
189
+ if (!this.data) return html``
190
+
191
+ const dataArray = Array.isArray(this.data) ? this.data : [this.data]
192
+
193
+ return html`
194
+ ${dataArray.map(
195
+ root => html`
196
+ <ul root>
197
+ ${this.renderItem(root)}
198
+ </ul>
199
+ `
200
+ )}
201
+ `
202
+ }
203
+
204
+ renderItem(item: TreeItem): TemplateResult {
205
+ const id = item[this.idProperty]
206
+ const label = item[this.labelProperty]
207
+ const children = item[this.childrenProperty] as TreeItem[]
208
+
209
+ const expandable = children && children.length > 0
210
+ const selected = id === this.selected?.[this.idProperty]
211
+
212
+ return html`
213
+ <li .item=${item}>
214
+ <span
215
+ ?selected=${selected}
216
+ @click=${this.onClickSelect.bind(this)}
217
+ @dblclick=${this.onClickSelectConfirm.bind(this)}
218
+ >
219
+ <div header>${id}</div>
220
+ <div label>${label}</div>
221
+ ${this.descriptionProperty ? html` <div description>${item[this.descriptionProperty]}&nbsp;</div>` : nothing}
222
+ </span>
223
+
224
+ ${expandable
225
+ ? html`
226
+ <ul>
227
+ ${children.map(child => this.renderItem(child))}
228
+ </ul>
229
+ `
230
+ : html``}
231
+ </li>
232
+ `
233
+ }
234
+
235
+ onClickSelect(e: MouseEvent) {
236
+ e.stopPropagation()
237
+
238
+ const target = e.target as HTMLElement
239
+ this.selected = (target.closest('li') as any)?.item
240
+
241
+ this.dispatchEvent(
242
+ new CustomEvent('select', {
243
+ detail: this.selected
244
+ })
245
+ )
246
+ }
247
+
248
+ onClickSelectConfirm(e: MouseEvent) {
249
+ e.stopPropagation()
250
+
251
+ const target = e.target as HTMLElement
252
+ this.selected = (target.closest('li') as any)?.item
253
+
254
+ this.dispatchEvent(
255
+ new CustomEvent('select-confirm', {
256
+ detail: this.selected
257
+ })
258
+ )
259
+ }
260
+ }
@@ -1,8 +1,8 @@
1
1
  /* Inspired by https://www.cssscript.com/clean-tree-diagram/ */
2
2
 
3
3
  import { html, css, LitElement, TemplateResult, nothing } from 'lit'
4
- import { customElement, property, state } from 'lit/decorators.js'
5
-
4
+ import { customElement, property } from 'lit/decorators.js'
5
+ import { ScrollbarStyles } from '@operato/styles'
6
6
  type TreeItem = {
7
7
  [key: string]: any
8
8
  __status__?: { [state: string]: boolean | string }
@@ -11,9 +11,11 @@ type TreeItem = {
11
11
  @customElement('ox-tree-vertical')
12
12
  export class TreeViewVertical extends LitElement {
13
13
  static styles = [
14
+ ScrollbarStyles,
14
15
  css`
15
16
  :host {
16
17
  display: block;
18
+ overflow: auto;
17
19
  }
18
20
 
19
21
  ul,
@@ -29,7 +31,8 @@ export class TreeViewVertical extends LitElement {
29
31
  text-align: center;
30
32
  }
31
33
 
32
- ul[root]:before {
34
+ ul[root]:before,
35
+ ul[root]:after {
33
36
  display: none;
34
37
  }
35
38
 
@@ -142,7 +145,7 @@ export class TreeViewVertical extends LitElement {
142
145
  `
143
146
  ]
144
147
 
145
- @property({ type: Object }) data?: TreeItem
148
+ @property({ type: Object }) data?: TreeItem | TreeItem[]
146
149
  @property({ type: Object }) selected?: TreeItem
147
150
  @property({ type: String, attribute: 'id-property' }) idProperty: string = 'id'
148
151
  @property({ type: String, attribute: 'label-property' }) labelProperty: string = 'label'
@@ -150,13 +153,19 @@ export class TreeViewVertical extends LitElement {
150
153
  @property({ type: String, attribute: 'children-property' }) childrenProperty: string = 'children'
151
154
 
152
155
  render() {
153
- return this.data
154
- ? html`
156
+ if (!this.data) return html``
157
+
158
+ const dataArray = Array.isArray(this.data) ? this.data : [this.data]
159
+
160
+ return html`
161
+ ${dataArray.map(
162
+ root => html`
155
163
  <ul root>
156
- ${this.renderItem(this.data)}
164
+ ${this.renderItem(root)}
157
165
  </ul>
158
166
  `
159
- : html``
167
+ )}
168
+ `
160
169
  }
161
170
 
162
171
  renderItem(item: TreeItem): TemplateResult {
@@ -0,0 +1,231 @@
1
+ import '../src/ox-tree-horizontal.js'
2
+ import '@material/web/icon/icon.js'
3
+
4
+ import { html, TemplateResult } from 'lit'
5
+
6
+ export default {
7
+ title: '3 modes in ox-tree-horizontal',
8
+ component: 'ox-tree-horizontal',
9
+ argTypes: {
10
+ data: { control: 'object' }
11
+ }
12
+ }
13
+
14
+ interface Story<T> {
15
+ (args: T): TemplateResult
16
+ args?: Partial<T>
17
+ argTypes?: Record<string, unknown>
18
+ }
19
+
20
+ type TreeItem = {
21
+ [key: string]: any
22
+ __status__?: { [state: string]: boolean | string }
23
+ }
24
+
25
+ interface ArgTypes {
26
+ data: Array<TreeItem>
27
+ }
28
+
29
+ const Template: Story<ArgTypes> = ({ data }: ArgTypes) => html`
30
+ <link
31
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
32
+ rel="stylesheet"
33
+ />
34
+ <link
35
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
36
+ rel="stylesheet"
37
+ />
38
+ <link
39
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
40
+ rel="stylesheet"
41
+ />
42
+
43
+ <link href="/themes/app-theme.css" rel="stylesheet" />
44
+ <link href="/themes/light.css" rel="stylesheet" />
45
+ <link href="/themes/dark.css" rel="stylesheet" />
46
+ <link href="/themes/spacing.css" rel="stylesheet" />
47
+ <link href="/themes/grist-theme.css" rel="stylesheet" />
48
+ <link href="/themes/form-theme.css" rel="stylesheet" />
49
+
50
+ <style>
51
+ ox-tree-horizontal {
52
+ height: 1000px;
53
+ }
54
+ </style>
55
+
56
+ <div>
57
+ <ox-tree-horizontal .data=${data} description-property="description"></ox-tree-horizontal>
58
+ </div>
59
+ `
60
+
61
+ export const Regular = Template.bind({})
62
+ Regular.args = {
63
+ data: [
64
+ {
65
+ id: 'H000',
66
+ label: 'Home',
67
+ children: [
68
+ {
69
+ id: 'H001',
70
+ label: 'About us',
71
+ children: [
72
+ {
73
+ id: 'H101',
74
+ label: 'Our history',
75
+ children: [
76
+ {
77
+ id: 'H111',
78
+ label: 'Founder',
79
+ description: 'long long founder history'
80
+ }
81
+ ]
82
+ },
83
+ {
84
+ id: 'H102',
85
+ label: 'Our board',
86
+ children: [
87
+ {
88
+ id: 'H121',
89
+ label: 'Brad Whiteman'
90
+ },
91
+ {
92
+ id: 'H122',
93
+ label: 'Cynthia Tolken'
94
+ },
95
+ {
96
+ id: 'H123',
97
+ label: 'Bobby Founderson'
98
+ }
99
+ ]
100
+ }
101
+ ]
102
+ },
103
+ {
104
+ id: 'H002',
105
+ label: 'Our products',
106
+ children: [
107
+ {
108
+ id: 'H201',
109
+ label: 'The Widget 2000™',
110
+ children: [
111
+ {
112
+ id: 'H211',
113
+ label: 'Order form'
114
+ }
115
+ ]
116
+ },
117
+ {
118
+ id: 'H202',
119
+ label: 'The McGuffin V2',
120
+ children: [
121
+ {
122
+ id: 'H221',
123
+ label: 'Order form'
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ id: 'H003',
131
+ label: 'Contact us',
132
+ children: [
133
+ {
134
+ id: 'H301',
135
+ label: 'Social media',
136
+ children: [
137
+ {
138
+ id: 'H311',
139
+ label: 'Facebook'
140
+ }
141
+ ]
142
+ }
143
+ ]
144
+ }
145
+ ]
146
+ },
147
+ {
148
+ id: 'H000',
149
+ label: 'Home',
150
+ children: [
151
+ {
152
+ id: 'H001',
153
+ label: 'About us',
154
+ children: [
155
+ {
156
+ id: 'H101',
157
+ label: 'Our history',
158
+ children: [
159
+ {
160
+ id: 'H111',
161
+ label: 'Founder',
162
+ description: 'long long founder history'
163
+ }
164
+ ]
165
+ },
166
+ {
167
+ id: 'H102',
168
+ label: 'Our board',
169
+ children: [
170
+ {
171
+ id: 'H121',
172
+ label: 'Brad Whiteman'
173
+ },
174
+ {
175
+ id: 'H122',
176
+ label: 'Cynthia Tolken'
177
+ },
178
+ {
179
+ id: 'H123',
180
+ label: 'Bobby Founderson'
181
+ }
182
+ ]
183
+ }
184
+ ]
185
+ },
186
+ {
187
+ id: 'H002',
188
+ label: 'Our products',
189
+ children: [
190
+ {
191
+ id: 'H201',
192
+ label: 'The Widget 2000™',
193
+ children: [
194
+ {
195
+ id: 'H211',
196
+ label: 'Order form'
197
+ }
198
+ ]
199
+ },
200
+ {
201
+ id: 'H202',
202
+ label: 'The McGuffin V2',
203
+ children: [
204
+ {
205
+ id: 'H221',
206
+ label: 'Order form'
207
+ }
208
+ ]
209
+ }
210
+ ]
211
+ },
212
+ {
213
+ id: 'H003',
214
+ label: 'Contact us',
215
+ children: [
216
+ {
217
+ id: 'H301',
218
+ label: 'Social media',
219
+ children: [
220
+ {
221
+ id: 'H311',
222
+ label: 'Facebook'
223
+ }
224
+ ]
225
+ }
226
+ ]
227
+ }
228
+ ]
229
+ }
230
+ ]
231
+ }