@operato/data-tree 8.0.0-beta.0 → 8.0.0-beta.2

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.
@@ -1,227 +0,0 @@
1
- /* Inspired by https://www.cssscript.com/clean-tree-diagram/ */
2
-
3
- import { html, css, LitElement, TemplateResult, nothing } from 'lit'
4
- import { customElement, property } from 'lit/decorators.js'
5
- import { ScrollbarStyles } from '@operato/styles'
6
- type TreeItem = {
7
- [key: string]: any
8
- __status__?: { [state: string]: boolean | string }
9
- }
10
-
11
- @customElement('ox-tree-vertical')
12
- export class TreeViewVertical extends LitElement {
13
- static styles = [
14
- ScrollbarStyles,
15
- css`
16
- :host {
17
- display: block;
18
- overflow: auto;
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: center;
32
- }
33
-
34
- ul[root]:before,
35
- ul[root]:after {
36
- display: none;
37
- }
38
-
39
- ul {
40
- display: table;
41
- }
42
-
43
- ul {
44
- width: 100%;
45
- }
46
-
47
- li {
48
- display: table-cell;
49
- padding: 1em 0;
50
- vertical-align: top;
51
- }
52
-
53
- li:before {
54
- outline: solid 1px #666;
55
- content: '';
56
- left: 0;
57
- position: absolute;
58
- right: 0;
59
- top: 0;
60
- }
61
-
62
- li:first-child:before {
63
- left: 50%;
64
- }
65
-
66
- li:last-child:before {
67
- right: 50%;
68
- }
69
-
70
- code,
71
- span {
72
- border: solid 0.1em #666;
73
- border-radius: 0.2em;
74
- display: inline-block;
75
- margin: 0 0.2em 1em;
76
- position: relative;
77
- min-width: 120px;
78
- }
79
-
80
- span[selected] {
81
- color: var(--md-sys-color-on-tertiary);
82
- background-color: var(--md-sys-color-tertiary);
83
- }
84
-
85
- ul:before,
86
- code:before,
87
- span:before {
88
- outline: solid 1px #666;
89
- content: '';
90
- height: 1em;
91
- left: 50%;
92
- position: absolute;
93
- }
94
-
95
- ul:before {
96
- top: -1em;
97
- }
98
-
99
- code:before,
100
- span:before {
101
- top: -1.1em;
102
- }
103
-
104
- ul[root] > li {
105
- margin-top: 0;
106
- }
107
-
108
- ul[root] > li:before,
109
- ul[root] > li:after,
110
- ul[root] > li > code:before,
111
- ul[root] > li > span:before {
112
- outline: none;
113
- }
114
-
115
- span {
116
- display: inline-block;
117
- background-color: var(--md-sys-color-surface);
118
-
119
- div[header] {
120
- font-weight: bold;
121
- font-size: 0.8em;
122
- color: var(--md-sys-color-on-primary, #fff);
123
- background-color: var(--md-sys-color-primary, #333);
124
- padding: 0.5em 1em;
125
- text-align: center;
126
- width: 100%;
127
- box-sizing: border-box;
128
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
129
- }
130
-
131
- div[label] {
132
- font-size: 1em;
133
- background-color: transparent;
134
- padding: 0.5em;
135
- text-align: left;
136
- }
137
-
138
- div[description] {
139
- font-size: 0.8em;
140
- background-color: transparent;
141
- padding: 0.5em;
142
- text-align: left;
143
- }
144
- }
145
- `
146
- ]
147
-
148
- @property({ type: Object }) data?: TreeItem | TreeItem[]
149
- @property({ type: Object }) selected?: TreeItem
150
- @property({ type: String, attribute: 'id-property' }) idProperty: string = 'id'
151
- @property({ type: String, attribute: 'label-property' }) labelProperty: string = 'label'
152
- @property({ type: String, attribute: 'description-property' }) descriptionProperty?: string
153
- @property({ type: String, attribute: 'children-property' }) childrenProperty: string = 'children'
154
-
155
- render() {
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`
163
- <ul root>
164
- ${this.renderItem(root)}
165
- </ul>
166
- `
167
- )}
168
- `
169
- }
170
-
171
- renderItem(item: TreeItem): TemplateResult {
172
- const id = item[this.idProperty]
173
- const label = item[this.labelProperty]
174
- const children = item[this.childrenProperty] as TreeItem[]
175
-
176
- const expandable = children && children.length > 0
177
- const selected = id === this.selected?.[this.idProperty]
178
-
179
- return html`
180
- <li .item=${item}>
181
- <span
182
- ?selected=${selected}
183
- @click=${this.onClickSelect.bind(this)}
184
- @dblclick=${this.onClickSelectConfirm.bind(this)}
185
- >
186
- <div header>${id}</div>
187
- <div label>${label}</div>
188
- ${this.descriptionProperty ? html` <div description>${item[this.descriptionProperty]}&nbsp;</div>` : nothing}
189
- </span>
190
-
191
- ${expandable
192
- ? html`
193
- <ul>
194
- ${children.map(child => this.renderItem(child))}
195
- </ul>
196
- `
197
- : html``}
198
- </li>
199
- `
200
- }
201
-
202
- onClickSelect(e: MouseEvent) {
203
- e.stopPropagation()
204
-
205
- const target = e.target as HTMLElement
206
- this.selected = (target.closest('li') as any)?.item
207
-
208
- this.dispatchEvent(
209
- new CustomEvent('select', {
210
- detail: this.selected
211
- })
212
- )
213
- }
214
-
215
- onClickSelectConfirm(e: MouseEvent) {
216
- e.stopPropagation()
217
-
218
- const target = e.target as HTMLElement
219
- this.selected = (target.closest('li') as any)?.item
220
-
221
- this.dispatchEvent(
222
- new CustomEvent('select-confirm', {
223
- detail: this.selected
224
- })
225
- )
226
- }
227
- }
package/src/ox-tree.ts DELETED
@@ -1,262 +0,0 @@
1
- import { LitElement, css, html, TemplateResult } from 'lit'
2
- import { customElement, property, state } from 'lit/decorators.js'
3
-
4
- type TreeItem = {
5
- [key: string]: any
6
- __status__?: { [state: string]: boolean | string }
7
- }
8
-
9
- @customElement('ox-tree')
10
- export class OxTree extends LitElement {
11
- static styles = [
12
- css`
13
- ul {
14
- list-style: none;
15
- padding-left: 18px;
16
- overflow: hidden;
17
- position: relative;
18
- }
19
-
20
- ul.root {
21
- padding-left: 0;
22
- }
23
-
24
- li {
25
- cursor: pointer;
26
- overflow: hidden;
27
- }
28
-
29
- div[self] {
30
- display: flex;
31
- align-items: center;
32
- gap: 6px;
33
- }
34
-
35
- span[expander] {
36
- display: inline-block;
37
- vertical-align: middle;
38
- width: 12px;
39
- height: 20px;
40
- cursor: pointer;
41
- position: relative;
42
- }
43
-
44
- span[expander]::before {
45
- position: absolute;
46
- top: 50%;
47
- left: 50%;
48
- transform: translate(-50%, -25%);
49
- content: ' ';
50
- border: 5px solid transparent;
51
- border-top: 5px solid var(--md-sys-color-on-primary-container, #1890ff);
52
- }
53
-
54
- li[collapsed] > div[self] > span[expander]::before {
55
- transform: translate(-25%, -50%) rotate(-90deg);
56
- }
57
-
58
- li[leaf] {
59
- padding-left: 18px;
60
- }
61
-
62
- span[checkbox] {
63
- display: inline-block;
64
- vertical-align: middle;
65
- width: 12px;
66
- height: 20px;
67
- cursor: pointer;
68
- position: relative;
69
- }
70
-
71
- span[checkbox]::before {
72
- cursor: pointer;
73
- position: absolute;
74
- top: 50%;
75
- left: 50%;
76
- transform: translate(-50%, -50%);
77
- content: ' ';
78
- display: block;
79
- width: 10px;
80
- height: 10px;
81
- border: 1px solid var(--md-sys-color-on-primary-container, #1890ff);
82
- border-radius: 2px;
83
- }
84
-
85
- span[label][selected] {
86
- background-color: var(--md-sys-color-on-primary-container, #1890ff);
87
- }
88
-
89
- li[checked='checked'] > div[self] > span[checkbox]::before {
90
- background-color: var(--md-sys-color-on-primary-container, #1890ff);
91
- border-color: var(--md-sys-color-on-primary-container, #1890ff);
92
- }
93
-
94
- li[checked='checked'] > div[self] > span[checkbox]::after {
95
- position: absolute;
96
- content: ' ';
97
- display: block;
98
- top: 50%;
99
- left: 50%;
100
- width: 3px;
101
- height: 7px;
102
- border: 2px solid #fff;
103
- border-top: none;
104
- border-left: none;
105
- -webkit-transform: translate(-50%, -50%) rotate(45deg);
106
- -ms-transform: translate(-50%, -50%) rotate(45deg);
107
- transform: translate(-50%, -50%) rotate(45deg);
108
- }
109
-
110
- li[checked='half-checked'] > div[self] > span[checkbox]::before {
111
- background-color: var(--md-sys-color-on-primary-container, #1890ff);
112
- border-color: var(--md-sys-color-on-primary-container, #1890ff);
113
- }
114
-
115
- li[checked='half-checked'] > div[self] > span[checkbox]::after {
116
- position: absolute;
117
- content: ' ';
118
- display: block;
119
- top: 50%;
120
- left: 50%;
121
- transform: translate(-50%, -50%);
122
- width: 10px;
123
- height: 2px;
124
- background-color: var(--md-sys-color-surface);
125
- }
126
- `
127
- ]
128
-
129
- @property({ type: Object }) data?: TreeItem
130
- @property({ type: Object }) selected?: TreeItem
131
- @property({ type: String, attribute: 'id-property' }) idProperty: string = 'id'
132
- @property({ type: String, attribute: 'label-property' }) labelProperty: string = 'label'
133
- @property({ type: String, attribute: 'children-property' }) childrenProperty: string = 'children'
134
-
135
- render() {
136
- return this.data
137
- ? html`
138
- <ul class="root">
139
- ${this.renderItem(this.data)}
140
- </ul>
141
- `
142
- : html``
143
- }
144
-
145
- renderItem(item: TreeItem): TemplateResult {
146
- const { __status__ = {} } = item
147
- const id = item[this.idProperty]
148
- const label = item[this.labelProperty]
149
- const children = item[this.childrenProperty] as TreeItem[]
150
-
151
- const expandable = children && children.length > 0
152
- const { collapsed, checked } = __status__
153
-
154
- const selected =
155
- this.selected !== undefined &&
156
- this.selected !== null &&
157
- id !== undefined &&
158
- id !== null &&
159
- id === this.selected[this.idProperty]
160
-
161
- return html`
162
- <li checked=${checked} ?collapsed=${collapsed} ?leaf=${!expandable} .item=${item}>
163
- <div self @click=${this.onClickSelect.bind(this)}>
164
- ${expandable ? html` <span expander @click=${this.onClickExpander.bind(this)}></span> ` : html``}
165
- <span checkbox @click=${this.onClickItem.bind(this)}></span>
166
- <span label ?selected=${selected}>${label}</span>
167
- </div>
168
- ${expandable && !collapsed
169
- ? html`
170
- <ul>
171
- ${children.map(child => this.renderItem(child))}
172
- </ul>
173
- `
174
- : html``}
175
- </li>
176
- `
177
- }
178
-
179
- onClickSelect(e: MouseEvent) {
180
- e.stopPropagation()
181
-
182
- const target = e.target as HTMLElement
183
- this.selected = (target.closest('li') as any)?.item
184
-
185
- this.dispatchEvent(
186
- new CustomEvent('select', {
187
- detail: this.selected
188
- })
189
- )
190
- }
191
-
192
- onClickExpander(e: MouseEvent) {
193
- e.stopPropagation()
194
-
195
- const target = e.target as HTMLSpanElement
196
- const itemElement = target!.closest('li') as HTMLLIElement
197
-
198
- var item = (itemElement as any).item
199
- item.__status__ = {
200
- ...item.__status__,
201
- collapsed: !itemElement.hasAttribute('collapsed')
202
- }
203
-
204
- this.requestUpdate()
205
- }
206
-
207
- onClickItem(e: MouseEvent) {
208
- e.stopPropagation()
209
-
210
- const target = e.target as HTMLSpanElement
211
- const itemElement = target!.closest('li') as HTMLLIElement
212
-
213
- var item = (itemElement as any).item
214
- var checked = itemElement.getAttribute('checked') || ''
215
-
216
- this.walkTreeCheckedUpdate(item, checked == '' || checked == 'unchecked' ? 'checked' : 'unchecked')
217
- this.updateCheckedAll(this.data!)
218
-
219
- this.requestUpdate()
220
- }
221
-
222
- walkTreeCheckedUpdate(item: TreeItem, checked: string = '') {
223
- const { __status__ } = item
224
- const children = item[this.childrenProperty] as TreeItem[]
225
-
226
- children?.forEach(child => this.walkTreeCheckedUpdate(child, checked))
227
- item.__status__ = {
228
- ...__status__,
229
- checked
230
- }
231
- }
232
-
233
- updateCheckedAll(item: TreeItem) {
234
- const { __status__ } = item
235
- const children = item[this.childrenProperty] as TreeItem[]
236
-
237
- if (!children || children.length == 0) {
238
- return
239
- }
240
-
241
- children.forEach(child => this.updateCheckedAll(child))
242
-
243
- var checked: string = ''
244
-
245
- children.forEach(child => {
246
- const { __status__ = {} } = child
247
-
248
- if (__status__.checked == 'half-checked') {
249
- checked = 'half-checked'
250
- } else if (__status__.checked == 'checked') {
251
- checked = checked == 'checked' || checked == '' ? 'checked' : 'half-checked'
252
- } else {
253
- checked = checked == 'unchecked' || checked == '' ? 'unchecked' : 'half-checked'
254
- }
255
- })
256
-
257
- item.__status__ = {
258
- ...__status__,
259
- checked
260
- }
261
- }
262
- }
@@ -1,231 +0,0 @@
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
- }