@qikdev/vue-ui 0.0.1 → 0.0.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.
Files changed (72) hide show
  1. package/package.json +6 -2
  2. package/src/components.js +209 -6
  3. package/src/content/browser.vue +477 -0
  4. package/src/content/item.vue +48 -0
  5. package/src/content/render/field.vue +423 -0
  6. package/src/content/render/group.vue +65 -0
  7. package/src/content/render/render-mixin.js +101 -0
  8. package/src/content/render/render.vue +86 -0
  9. package/src/filter/FilterBuilder.vue +147 -0
  10. package/src/filter/FilterCondition.vue +335 -0
  11. package/src/filter/FilterRule.vue +257 -0
  12. package/src/form/expressions/index.js +83 -0
  13. package/src/form/field.vue +624 -0
  14. package/src/form/form.vue +280 -0
  15. package/src/form/getDefaultValue.js +224 -0
  16. package/src/form/inputs/button-select.vue +208 -0
  17. package/src/form/inputs/checkbox.vue +91 -0
  18. package/src/form/inputs/content-select.vue +187 -0
  19. package/src/form/inputs/currency.vue +205 -0
  20. package/src/form/inputs/datefield.vue +132 -0
  21. package/src/form/inputs/group.vue +155 -0
  22. package/src/form/inputs/input-mixin.js +440 -0
  23. package/src/form/inputs/native-select-old.vue +43 -0
  24. package/src/form/inputs/object-field.vue +50 -0
  25. package/src/form/inputs/option.vue +19 -0
  26. package/src/form/inputs/options-manager.vue +244 -0
  27. package/src/form/inputs/phone-number-input.vue +235 -0
  28. package/src/form/inputs/search.vue +117 -0
  29. package/src/form/inputs/select.vue +211 -0
  30. package/src/form/inputs/switch.vue +87 -0
  31. package/src/form/inputs/textarea.vue +80 -0
  32. package/src/form/inputs/textfield.vue +165 -0
  33. package/src/form/inputs/timezone.vue +642 -0
  34. package/src/form/inputs/upload/filedrop.vue +72 -0
  35. package/src/form/inputs/upload/upload.vue +323 -0
  36. package/src/form/parseBoolean.js +24 -0
  37. package/src/layout/flex-body.vue +3 -2
  38. package/src/layout/flex-cell.vue +45 -0
  39. package/src/layout/flex-column.vue +1 -1
  40. package/src/layout/flex-footer.vue +3 -2
  41. package/src/layout/flex-header.vue +3 -2
  42. package/src/layout/flex-row.vue +35 -0
  43. package/src/layout/flex-spacer.vue +17 -0
  44. package/src/layout/panel-body.vue +13 -0
  45. package/src/layout/panel-footer.vue +20 -0
  46. package/src/layout/panel-header.vue +20 -0
  47. package/src/layout/panel.vue +23 -0
  48. package/src/modal/ConfirmModal.vue +50 -0
  49. package/src/modal/ContentModal.vue +99 -0
  50. package/src/modal/Modal.vue +85 -0
  51. package/src/modal/ModalMixin.js +21 -0
  52. package/src/modal/OptionsModal.vue +31 -0
  53. package/src/modal/PromptModal.vue +31 -0
  54. package/src/services/selection.js +140 -0
  55. package/src/table/Table.vue +269 -0
  56. package/src/table/TableCell.vue +64 -0
  57. package/src/table/TableRow.vue +94 -0
  58. package/src/table/cells/TableCellMixin.js +15 -0
  59. package/src/table/cells/Thumbnail.vue +38 -0
  60. package/src/ui/button.vue +254 -0
  61. package/src/ui/checkbox.vue +79 -0
  62. package/src/ui/icon.vue +57 -0
  63. package/src/ui/image.vue +158 -0
  64. package/src/ui/link.vue +62 -0
  65. package/src/ui/list-item.vue +16 -0
  66. package/src/ui/list.vue +26 -0
  67. package/src/ui/menu.vue +135 -0
  68. package/src/ui/progressbar.vue +77 -0
  69. package/src/ui/spinner.vue +26 -0
  70. package/src/ui/switch.vue +89 -0
  71. package/yarn-error.log +2923 -0
  72. package/index.js +0 -14
@@ -0,0 +1,624 @@
1
+ <template>
2
+ <div class="ux-field" @focusin="focus" @focusout="blur" v-if="visible" :class="classes">
3
+ <template v-if="widget == 'checkbox'">
4
+ <checkbox @touched="touch" :field="actualField" v-model="fieldModel" />
5
+ </template>
6
+ <template v-if="widget == 'switch'">
7
+ <boolean-switch @touched="touch" :field="actualField" v-model="fieldModel" />
8
+ </template>
9
+ <template v-if="widget == 'upload'">
10
+ <upload @touched="touch" :field="actualField" v-model="fieldModel" />
11
+ </template>
12
+ <template v-if="widget == 'group'">
13
+ <template v-if="asObject">
14
+ <field-group @form:state="groupStateAltered" ref="group" @touched="touch" :field="actualField" :parentModel="parentModel" v-model="fieldModel" />
15
+ </template>
16
+ <template v-else>
17
+ <field-group @form:state="groupStateAltered" ref="group" @touched="touch" :field="actualField" :parentModel="parentModel" v-model="sourceModel" />
18
+ </template>
19
+ </template>
20
+ <template v-if="widget == 'select'">
21
+ <native-select @touched="touch" :field="actualField" v-model="fieldModel" />
22
+ </template>
23
+ <template v-if="widget == 'button'">
24
+ <button-select @touched="touch" :field="actualField" v-model="fieldModel" />
25
+ </template>
26
+ <template v-if="widget == 'textfield'">
27
+ <text-field @touched="touch" :field="actualField" v-model="fieldModel" />
28
+ </template>
29
+ <template v-if="widget == 'currency'">
30
+ <currency-field @touched="touch" :field="actualField" v-model="fieldModel" />
31
+ </template>
32
+ <template v-if="widget == 'datefield'">
33
+ <date-field @touched="touch" :field="actualField" v-model="fieldModel" />
34
+ </template>
35
+ <template v-if="widget == 'content-select'">
36
+ <content-select @touched="touch" :field="actualField" v-model="fieldModel" />
37
+ </template>
38
+ <template v-if="widget == 'richtext'">
39
+ <text-area @touched="touch" :field="actualField" v-model="fieldModel" />
40
+ </template>
41
+ <template v-if="widget == 'textarea'">
42
+ <text-area @touched="touch" :field="actualField" v-model="fieldModel" />
43
+ </template>
44
+ <template v-if="widget == 'timezone'">
45
+ <timezone-select @touched="touch" :field="actualField" v-model="fieldModel" />
46
+ </template>
47
+ <template v-if="widget == 'phone'">
48
+ <phone-number-input @touched="touch" :field="actualField" v-model="fieldModel" />
49
+ </template>
50
+ <template v-if="widget == 'value'">
51
+ </template>
52
+ <template v-if="widget == 'object'">
53
+ <object-field @touched="touch" :field="actualField" v-model="fieldModel" />
54
+ </template>
55
+ <template v-if="widget == 'options'">
56
+ <options-manager @touched="touch" :field="actualField" v-model="fieldModel" />
57
+ </template>
58
+ <div v-if="error && validateResults.message" class="ux-field-message">
59
+ {{validateResults.message}}
60
+ </div>
61
+ <!-- <pre>Has Data: {{hasData}}</pre>
62
+ <pre>Dirty: {{dirty}}</pre>
63
+ <pre>Dirty Before: {{isDirtyBeforeInput}}</pre>
64
+ <pre>Touched: {{touched}}</pre>
65
+ <pre>DEFAULT VALUE: {{fieldDefaultValue}}</pre> -->
66
+ </div>
67
+ </template>
68
+ <script>
69
+ //Inputs
70
+ import PhoneNumberInput from './inputs/phone-number-input.vue';
71
+ import TimezoneSelect from './inputs/timezone.vue';
72
+ import ContentSelect from './inputs/content-select.vue';
73
+ import CurrencyField from './inputs/currency.vue';
74
+ import TextField from './inputs/textfield.vue';
75
+ import TextArea from './inputs/textarea.vue';
76
+ import DateField from './inputs/datefield.vue';
77
+ import Checkbox from './inputs/checkbox.vue';
78
+ import Switch from './inputs/switch.vue';
79
+ import Upload from './inputs/upload/upload.vue';
80
+ import FieldGroup from './inputs/group.vue';
81
+ import NativeSelect from './inputs/select.vue';
82
+ import ButtonSelect from './inputs/button-select.vue';
83
+ import ObjectField from './inputs/object-field.vue';
84
+ import OptionsManager from './inputs/options-manager.vue';
85
+
86
+ ////////////////
87
+
88
+ import Expressions from './expressions';
89
+ import getDefaultValue from './getDefaultValue';
90
+ import parseBoolean from './parseBoolean';
91
+
92
+ ////////////////////////////////////////
93
+
94
+ function hasExpression(key) {
95
+ return function() {
96
+ var self = this;
97
+ if (!self.expressions) {
98
+ return;
99
+ }
100
+
101
+ let expression = self.expressions[key];
102
+ if (expression) {
103
+ return true;
104
+ }
105
+ }
106
+ }
107
+
108
+ ////////////////////////////////////////
109
+
110
+ function computedExpression(key) {
111
+ return function() {
112
+ var self = this;
113
+ if (!self.expressions) {
114
+ return;
115
+ }
116
+
117
+ let expression = self.expressions[key];
118
+ if (!expression) {
119
+ return;
120
+ }
121
+
122
+ let context = self.expressionsContext;
123
+ return Expressions.evaluateExpression(expression, context);
124
+ }
125
+ }
126
+
127
+
128
+ export default {
129
+ components: {
130
+ ButtonSelect,
131
+ NativeSelect,
132
+ DateField,
133
+ TextField,
134
+ CurrencyField,
135
+ TextArea,
136
+ Checkbox,
137
+ BooleanSwitch: Switch,
138
+ FieldGroup,
139
+ ContentSelect,
140
+ TimezoneSelect,
141
+ PhoneNumberInput,
142
+ Upload,
143
+ ObjectField,
144
+ OptionsManager,
145
+ },
146
+ props: {
147
+ field: {
148
+ type: Object,
149
+ required: true,
150
+ },
151
+ parentModel: {
152
+ type: Object,
153
+ },
154
+ modelValue: {
155
+ type: Object,
156
+ required: true,
157
+ },
158
+ },
159
+ data() {
160
+
161
+
162
+ return {
163
+ defaultValue: null,
164
+ model: this.modelValue,
165
+ touched: false,
166
+ focussed: false,
167
+ validateResults: { valid: true },
168
+ mounted: false,
169
+ subFormState: {},
170
+ isDirty: false,
171
+ isDirtyBeforeInput: false,
172
+ }
173
+ },
174
+
175
+ created() {
176
+ this.checkDirtyState();
177
+ },
178
+ mounted() {
179
+ this.mounted = true;
180
+ },
181
+ beforeUnmount() {
182
+ this.mounted = false;
183
+ },
184
+ methods: {
185
+ checkDirtyState() {
186
+
187
+ //What is the value for this field right now?
188
+ var existingData = this.fieldModel;
189
+ var proposedDefaultValue = this.fieldDefaultValue;
190
+
191
+ ///////////////////////////////////////////
192
+
193
+ var existingString = JSON.stringify(this.cleanOutput(existingData));
194
+ var proposedString = JSON.stringify(this.cleanOutput(proposedDefaultValue));
195
+
196
+ //We already have data
197
+
198
+
199
+ if (existingData && (existingString != proposedString)) {
200
+ this.isDirty = true;
201
+ this.isDirtyBeforeInput = true;
202
+ //May as well put it in anyway so it can clean itself if need be
203
+ this.fieldModel = existingData;
204
+ } else {
205
+ //The field is untouched
206
+ this.isDirty = false;
207
+ this.isDirtyBeforeInput = false;
208
+
209
+ //Use the default
210
+ this.fieldModel = proposedDefaultValue;
211
+ }
212
+
213
+ // ///////////////////////////////////////////
214
+
215
+ // var defaultValue = currentValue || proposedDefaultValue
216
+ // if (!currentValue) {
217
+ // currentValue = defaultValue;
218
+ // }
219
+
220
+ // ///////////////////////////////////////////
221
+
222
+ // this.fieldModel = currentValue;
223
+
224
+ },
225
+ groupStateAltered(details) {
226
+ this.subFormState = details;
227
+ },
228
+ focus() {
229
+ this.focussed = true;
230
+ },
231
+ blur() {
232
+ this.focussed = false;
233
+ if (this.expressions && this.expressions.value) {
234
+ //Reset the value after we blur out
235
+ this.fieldModel = this.getExpressionValue;
236
+ }
237
+ },
238
+ reset() {
239
+ // var defaultValue = this.expressions && this.expressions.defaultValue ? this.getExpressionDefaultValue : getDefaultValue(this.actualField);
240
+ // this.fieldModel = defaultValue;
241
+ this.touched = false;
242
+ },
243
+ touch() {
244
+ this.touched = true;
245
+ },
246
+ cleanInput(val) {
247
+ return val;
248
+ },
249
+ cleanOutput(val) {
250
+ return val;
251
+ }
252
+ },
253
+ watch: {
254
+ mounted(val) {
255
+ if (val) {
256
+ this.$emit('field:mount', this);
257
+ } else {
258
+ this.$emit('field:unmount', this);
259
+ }
260
+ },
261
+ visible(now) {
262
+ //Remove and clear all data
263
+ if (!now) {
264
+ this.touched = false;
265
+ if (this.expressions && this.expressions.value) {
266
+ this.fieldModel = this.getExpressionValue
267
+ } else {
268
+ this.fieldModel = undefined;
269
+ }
270
+
271
+ } else {
272
+ this.reset();
273
+ }
274
+
275
+ },
276
+ focussed(val) {
277
+ if (val) {
278
+ this.$emit('field:focus', this);
279
+ } else {
280
+ this.$emit('field:blur', this);
281
+ }
282
+ },
283
+ invalid(val) {
284
+ this.$emit('field:invalid', this);
285
+ },
286
+ valid(val) {
287
+ this.$emit('field:valid', this);
288
+ },
289
+ error(val) {
290
+ this.$emit('field:error', this);
291
+ },
292
+ touched(val) {
293
+ this.$emit('field:touched', this);
294
+ },
295
+ dirty(val) {
296
+ this.$emit('field:dirty', this);
297
+ },
298
+ changeString(v) {
299
+ this.validateResults = this.$qik.content.validateField(this.fieldModel, this.actualField);
300
+ },
301
+ modelValue(val, old) {
302
+ this.model = val;
303
+ this.touched = false;
304
+ this.checkDirtyState();
305
+ },
306
+ getExpressionHide(result) {
307
+
308
+ },
309
+ getExpressionDefaultValue(result) {
310
+
311
+ //If this already had data, or has been touched and now has data
312
+ if (this.isDirtyBeforeInput || (this.touched && this.dirty)) {
313
+ //Don't update it
314
+ } else {
315
+ this.fieldModel = result;
316
+ }
317
+
318
+ },
319
+ getExpressionValue(result) {
320
+ this.fieldModel = result;
321
+ },
322
+ },
323
+ computed: {
324
+ fieldDefaultValue() {
325
+ return this.cleanInput(this.expressions && this.expressions.defaultValue ? this.getExpressionDefaultValue : getDefaultValue(this.actualField));
326
+ },
327
+ title() {
328
+ return this.field.title;
329
+ },
330
+ actualField() {
331
+
332
+ var field = this.field;
333
+ if (this.getExpressionRequired) {
334
+ return Object.assign({}, field, { minimum: 1 });
335
+ }
336
+
337
+ return field;
338
+ },
339
+ changeString() {
340
+ return `${JSON.stringify(this.fieldModel)}-${this.actualField.minimum}`;
341
+ },
342
+ valid() {
343
+ return !this.invalid;
344
+ },
345
+ error() {
346
+ return !this.focussed && this.touched && this.invalid;
347
+ },
348
+ invalid() {
349
+ //Check the subform
350
+ var invalidSubForm = this.subFormState && this.subFormState.invalid;
351
+ if (invalidSubForm) {
352
+
353
+ return { invalidSubForm: this.subFormState, mounted: this.mounted };
354
+ }
355
+
356
+ //Check if our validator says this field is valid
357
+ var isInvalid = !this.validateResults.valid;
358
+ // if (isInvalid) {
359
+
360
+ // }
361
+
362
+
363
+
364
+ return isInvalid;
365
+ },
366
+ dirty() {
367
+
368
+ return this.isDirty;
369
+
370
+ },
371
+ hasData() {
372
+
373
+ if (typeof this.fieldModel === 'undefined') {
374
+ return false;
375
+ }
376
+
377
+ if (this.multiValue) {
378
+ if (!this.fieldModel || !this.fieldModel.length) {
379
+ return false;
380
+ }
381
+ }
382
+
383
+ if (this.fieldModel == '') {
384
+ return false;
385
+ }
386
+
387
+ // if (this.hasExpressionDefaultValue) {
388
+ // return this.touched && (this.fieldModel != this.getExpressionDefaultValue);
389
+ // }
390
+
391
+ return true;
392
+ },
393
+ getExpressionHide() {
394
+ if (!this.expressions) {
395
+ return;
396
+ }
397
+
398
+ let showExpression = this.expressions.show;
399
+ let hideExpression = this.expressions.hide;
400
+ let context = this.expressionsContext;
401
+
402
+ if (showExpression) {
403
+ return !!!Expressions.evaluateExpression(showExpression, context);
404
+ } else if (hideExpression) {
405
+ return Expressions.evaluateExpression(hideExpression, context);
406
+ }
407
+ },
408
+ getExpressionRequired: computedExpression('required'),
409
+ getExpressionDefaultValue: computedExpression('defaultValue'),
410
+ getExpressionValue: computedExpression('value'),
411
+ hasExpressionDefaultValue: hasExpression('defaultValue'),
412
+ expressions() {
413
+ return this.field.expressions;
414
+ },
415
+ expressionsContext() {
416
+ return {
417
+ this: this.model,
418
+ model: this.model,
419
+ data: this.parentModel,
420
+ }
421
+ },
422
+ hidden() {
423
+ if (this.actualField.readOnly) {
424
+ return true;
425
+ }
426
+ return this.getExpressionHide;
427
+ },
428
+ visible() {
429
+ return this.mounted && !this.hidden;
430
+ },
431
+ type() {
432
+ return this.actualField.type || 'string';
433
+ },
434
+ key() {
435
+ return this.actualField.key;
436
+ },
437
+ isGroup() {
438
+ return this.type === 'group'
439
+ },
440
+ asObject() {
441
+ return this.isGroup && this.actualField.asObject;
442
+ },
443
+ layoutGroup() {
444
+ return this.isGroup && !this.actualField.asObject;
445
+ },
446
+ fieldModel: {
447
+ get() {
448
+ return this.cleanOutput(this.model[this.key]);
449
+ },
450
+ set(value) {
451
+
452
+ var cleaned = this.cleanInput(value);
453
+
454
+ if (this.model[this.key] != cleaned) {
455
+ this.model[this.key] = cleaned
456
+ this.isDirty = true;
457
+ this.$emit('update:modelValue', this.model);
458
+ }
459
+ }
460
+ },
461
+ sourceModel: {
462
+ get() {
463
+ return this.model;
464
+ },
465
+ set(value) {
466
+ this.model = value;
467
+ this.$emit('update:modelValue', this.model);
468
+ }
469
+ },
470
+ classes() {
471
+ var array = [];
472
+ array.push(`ux-field-${this.type}`)
473
+
474
+ if (this.layoutGroup) {
475
+ array.push('ux-layout-only');
476
+ }
477
+
478
+ if (this.touched) {
479
+ array.push('ux-field-touched');
480
+ }
481
+
482
+ if (this.dirty) {
483
+ array.push('ux-field-dirty');
484
+ }
485
+
486
+ if (this.valid) {
487
+ array.push('ux-field-valid');
488
+ }
489
+
490
+ if (this.invalid) {
491
+ array.push('ux-field-invalid');
492
+ }
493
+
494
+ if (this.error) {
495
+ array.push('ux-field-error');
496
+ }
497
+
498
+ return array;
499
+ },
500
+ widget() {
501
+
502
+ //We're a group
503
+ if (this.type == 'group') {
504
+ return this.type;
505
+ }
506
+
507
+ //Get the widget
508
+ var widget = this.actualField.widget;
509
+
510
+ ///////////////////////////////
511
+
512
+
513
+ ///////////////////////////////
514
+
515
+ switch (widget) {
516
+ case 'content-select':
517
+ case 'select':
518
+ case 'checkbox':
519
+ case 'datefield':
520
+ case 'richtext':
521
+ case 'textarea':
522
+ case 'switch':
523
+ case 'email':
524
+ case 'url':
525
+ case 'currency':
526
+ case 'timezone':
527
+ case 'country':
528
+ case 'typeselect':
529
+ case 'upload':
530
+ case 'options':
531
+ case 'button':
532
+ break;
533
+ case 'phone':
534
+ case 'phonenumber':
535
+ return 'phone'
536
+ break;
537
+
538
+ default:
539
+ switch (this.type) {
540
+ case 'date':
541
+ widget = 'datefield';
542
+ break;
543
+ case 'reference':
544
+ widget = 'content-select';
545
+ break;
546
+ case 'boolean':
547
+ widget = 'checkbox';
548
+ break;
549
+ default:
550
+ case 'string':
551
+ widget = 'textfield';
552
+ break;
553
+ case 'object':
554
+ widget = 'object';
555
+ break;
556
+ }
557
+ break;
558
+ }
559
+
560
+ return widget;
561
+
562
+ },
563
+ },
564
+
565
+ }
566
+ </script>
567
+ <style lang="scss" scoped>
568
+ .ux-field {
569
+ margin-bottom: 1.5em;
570
+
571
+ &.ux-layout-only {
572
+ margin-bottom: 0;
573
+ }
574
+
575
+ .ux-field-message {
576
+ border: red;
577
+ background: rgba(red, 0.1);
578
+ color: red;
579
+ font-size: 0.7em;
580
+ padding: 0.3em 0.5em;
581
+ border-radius: 0 1em 1em;
582
+ margin: 0.3em 0;
583
+ display: inline-block;
584
+ }
585
+
586
+ // &.ux-field-error {
587
+ // .ux-field-message {
588
+ // visibility: visible;
589
+ // }
590
+ // }
591
+
592
+
593
+
594
+ }
595
+
596
+
597
+
598
+ :deep(.ux-field-title) {
599
+ // .ux-field-title {
600
+ margin-top: 0.5em;
601
+ display: block;
602
+ margin-bottom: 0.2em;
603
+ font-size: 0.9em;
604
+ font-weight: bold;
605
+ }
606
+
607
+
608
+ :deep(.ux-field-description) {
609
+ // .ux-field-description {
610
+
611
+ font-size: clamp(12px, 0.8em, 16px);
612
+ opacity: 0.5;
613
+ margin-bottom: 0.5em;
614
+
615
+ }
616
+
617
+ .ux-field.ux-field-error> :deep(.ux-field-title) {
618
+ color: red;
619
+ }
620
+
621
+ :deep(.ux-form-flex .ux-field-description) {
622
+ // min-height: 2.6em;
623
+ }
624
+ </style>