-
Notifications
You must be signed in to change notification settings - Fork 1
/
leather-editable-list.html
277 lines (243 loc) · 8.74 KB
/
leather-editable-list.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-checkbox/paper-checkbox.html">
<link rel="import" href="../iron-icons/iron-icons.html">
<link rel="import" href="../paper-toolbar/paper-toolbar.html">
<!--
`<leather-editable-list>` provides a simple editable list where items can be added and removed from.
Usage
===
Usage is pretty straightforward. You set the element up with an `[items]` property referring to an array of items. Each
items is either generated by it's `toString()` method or a custom `<template>` you can specify. Adding items can be done
in two different ways:
- The first option is having a + button in the top right corner which will add an item to the array with the value
specified in `[default]`
- The second option is providing a child element to `<leather-editable-list>` with `[add-row]` set on it. This element
will then be shown under the list with a + button to the right of it. When the + button is clicked it will trigger
either the `.value` of the `[add-row]` element to be added or alternatively the `[new-value]` property of
`<leather-editable-list>` to be used.
If this sounds confusing just check out the demo page, it's pretty simple.
@group Leather Elements
@element leather-editable-list
@demo demo/index.html
@hero hero.svg
-->
<dom-module id="leather-editable-list">
<template>
<style>
:host {
display: block;
box-sizing: border-box;
}
.row {
display: flex;
align-items: center;
padding:5px;
}
.row .item{
flex: 1;
margin-right:11px;
}
.toolbar{
display: flex;
align-items: center;
background-color: var(--dark-primary-color);
color: var(--text-primary-color);
padding: 5px;
min-height: 40px;
}
.toolbar .title{
flex: 1;
padding-left: 10px;
font-family: Roboto;
}
.toolbar .icon{
}
.toolbar paper-icon-button{
display: none;
}
.toolbar paper-icon-button[data-visible]{
display: block;
}
</style>
<div>
<div class="toolbar">
<div class="title">{{heading}}</div>
<template is="dom-if" if="{{!_isUndefined(default)}}">
<paper-icon-button icon="add" on-tap="addDefault" data-visible$="[[!_anyChecked(selectedItems.splices)]]">+</paper-icon-button>
</template>
<paper-icon-button icon="delete" on-tap="deleteSelected" data-visible$="[[_anyChecked(selectedItems.splices)]]"></paper-icon-button>
</div>
<template is="dom-repeat" items="{{items}}" on-dom-change="_restructureItems" index-as="rowNumber">
<div class="row">
<div class="item" data-item="[[item]]" data-empty></div>
<div class="icon">
<paper-checkbox checked="[[_isChecked(item, rowNumber, _recheckChecked)]]" on-change="_setSelection"></paper-checkbox>
</div>
</div>
</template>
<template is="dom-if" if="{{_showAddRowInterface()}}">
<div class="row">
<div class="item">
<content select="[add-row]"></content>
</div>
<div class="icon">
<paper-icon-button icon="add" on-tap="add" data-visible$="[[!_anyChecked(selectedItems.splices)]]">+</paper-icon-button>
</div>
</div>
</template>
</div>
</template>
<script>
Polymer({
is: 'leather-editable-list',
properties: {
/*
Array of items to be shown in the list.
*/
items: Array,
/*
Items currently selected.
*/
selectedItems: Array,
/*
The default value to be used when a new element is added
*/
default: {
type: Object
},
/*
The title to show at the top of the component. Depending on the style of the rest of the application it might make sense to leave this empty.
*/
heading: {
type: String,
value: ''
},
/*
If the element `[add-row]` exposes a `value` property itself (e.g. because it's paper-input) then that will be used, otherwise you should
bind whatever value it exposes to this property.
*/
newValue: {
type: Object
},
/*
this is a hack as _isChecked(item, items.splices) doesn't work
*/
_recheckChecked: {
type: Number,
computed: '_getRecheckChecked(items.splices)'
}
},
behaviors: [
Polymer.Templatizer
],
observers: [
'_notifyPathOnInstances(items.*)'
],
ready: function() {
var template = Polymer.dom(this).querySelector('template');
this._instanceProps = {};
this._instanceProps.value = true;
this.templatize(template);
this.set('selectedItems', []);
if(this.newValue){
this._initialNewValue = JSON.parse(JSON.stringify(this.newValue));
}
var addRow = Polymer.dom(this).querySelector('[add-row]');
if(addRow && typeof addRow.value !== 'undefined'){
this._initialAddRowValue = addRow.value;
}
},
attached: function() {
},
detached: function() {
},
_restructureItems: function(){
var items = Polymer.dom(this.root).querySelectorAll('.row .item');
var item;
for(var i=0; i<items.length; i++){
item = items[i];
if(item.hasAttribute('data-empty')){
item.removeAttribute('data-empty');
var instance = this.stamp({value: item.dataItem});
instance.collectionKey = Polymer.Collection.get(this.items).getKey(item.dataItem);
item.setAttribute('data-collection-key', instance.collectionKey);
item.appendChild(instance.root);
item.instance = instance;
}
}
},
_notifyPathOnInstances: function(change){
var path = change.path.split('.');
if(path.length >= 2 && path[1].substr(0,1) == '#'){
var value = this.get('items.'+path[1]);
var instance = Polymer.dom(this.root).querySelector('.row .item[data-collection-key="'+path[1]+'"]').instance;
instance.set('value', value);
}
},
_isChecked: function(item, id){
return this.selectedItems.indexOf(id) > -1;
},
deleteSelected: function(){
this.selectedItems.sort();
for(var i=this.selectedItems.length-1;i>=0;i--){
this.splice('items', this.selectedItems[i], 1);
this.splice('selectedItems', i, 1);
}
},
add: function(){
var addRow = Polymer.dom(this).querySelector("[add-row]");
if(addRow.value){
this.push('items', JSON.parse(JSON.stringify(addRow.value)));
addRow.value = JSON.parse(JSON.stringify(this._initialAddRowValue));
}else{
this.push('items', JSON.parse(JSON.stringify(this.newValue)));
this.newValue = JSON.parse(JSON.stringify(this._initialNewValue))
}
},
addDefault: function(){
this.push('items', JSON.parse(JSON.stringify(this.default)));
},
_setSelection: function(ev){
var id = ev.model.rowNumber;
if(ev.target.checked){
this.push('selectedItems', id);
}else{
this.splice('selectedItems', this.selectedItems.indexOf(id), 1);
}
},
_forwardParentProp: function(prop, value){
console.warn('_forwardParentProp',arguments);
},
_forwardParentPath: function(prop, value){
console.warn('_forwardParentPath',arguments);
},
_forwardInstanceProp: function(templateInstance, prop, value){
var path = prop.split('.');
var item = path.shift();
var forwardPath = ['items', templateInstance.collectionKey].join('.');
this.set(forwardPath, value);
//console.info('_forwardInstanceProp',arguments, templateInstance.i);
},
_forwardInstancePath: function(templateInstance, prop, value){
var path = prop.split('.');
var item = path.shift();
var forwardPath = ['items', templateInstance.collectionKey].concat(path).join('.');
this.set(forwardPath, value);
//console.info('_forwardInstancePath',arguments, templateInstance.i);
},
_getRecheckChecked: function(){
return Symbol();
},
_anyChecked: function(){
return this.selectedItems.length > 0;
},
_showAddRowInterface: function(){
return Polymer.dom(this).querySelector("[add-row]") ? true : false;
},
_isUndefined: function(obj){
return typeof obj === 'undefined';
}
});
</script>
</dom-module>