@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,244 @@
1
+ <template>
2
+ <label class="ux-field-title" v-if="showLabel">{{label}} <span class="ux-required-marker" v-if="required">*</span></label>
3
+ <div class="ux-field-description" v-if="showDescription">{{description}}</div>
4
+ <div v-if="multiValue">
5
+ <flex-row class="ux-text-row" :key="index" v-for="(entry, index) in model">
6
+ <flex-cell>
7
+ <div class="ux-text-wrap prefixed">
8
+ <span class="ux-text-prefix">Label</span>
9
+ <input v-if="lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Label" @focus="touch" ref="input" @keydown.enter.stop.prevent="add()" @blur="titleBlurred(index)" @update:modelValue="entryTitleChanged(index)" v-model.lazy="model[index].title" />
10
+ <input v-if="!lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Label" @focus="touch" ref="input" @keydown.enter.stop.prevent="add()" @blur="titleBlurred(index)" @update:modelValue="entryTitleChanged(index)" v-model="model[index].title" />
11
+ </div>
12
+ </flex-cell>
13
+ <flex-cell>
14
+ <div class="ux-text-wrap prefixed">
15
+ <span class="ux-text-prefix">Value</span>
16
+ <input v-if="lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Value" @focus="valueTouched(index)" ref="valueInput" @keydown.enter.stop.prevent="add()" v-model.lazy="model[index].value" />
17
+ <input v-if="!lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Value" @focus="valueTouched(index)" ref="valueInput" @keydown.enter.stop.prevent="add()" v-model="model[index].value" />
18
+ </div>
19
+ </flex-cell>
20
+ <flex-cell shrink vcenter>
21
+ <ux-button tag="a" icon v-if="canRemoveValue" @click="remove(entry)">
22
+ <ux-icon icon="fa-times" />
23
+ </ux-button>
24
+ </flex-cell>
25
+ </flex-row>
26
+ <ux-button v-if="canAddValue" @click="add()">{{addLabel}}
27
+ <ux-icon icon="fa-plus" right />
28
+ </ux-button>
29
+ </div>
30
+ <template v-else>
31
+ <flex-row class="ux-text-row">
32
+ <flex-cell>
33
+ <div class="ux-text-wrap prefixed">
34
+ <span class="ux-text-prefix">Label</span>
35
+ <input v-if="lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Label" @focus="touch" ref="input" @keydown.enter.stop.prevent="add()" @blur="titleBlurred(index)" @update:modelValue="entryTitleChanged" v-model.lazy="model.title" />
36
+ <input v-if="!lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Label" @focus="touch" ref="input" @keydown.enter.stop.prevent="add()" @blur="titleBlurred(index)" @update:modelValue="entryTitleChanged" v-model="model.title" />
37
+ </div>
38
+ </flex-cell>
39
+ <flex-cell>
40
+ <div class="ux-text-wrap prefixed">
41
+ <span class="ux-text-prefix">Value</span>
42
+ <input v-if="lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Value" @focus="valueTouched" ref="valueInput" @keydown.enter.stop.prevent="add()" v-model.lazy="model.value" />
43
+ <input v-if="!lazy" class="ux-field-focus ux-text-input-multiple" placeholder="Value" @focus="valueTouched" ref="valueInput" @keydown.enter.stop.prevent="add()" v-model="model.value" />
44
+ </div>
45
+ </flex-cell>
46
+ </flex-row>
47
+ </template>
48
+ </template>
49
+ <script>
50
+ import InputMixin from './input-mixin';
51
+
52
+
53
+
54
+
55
+ export default {
56
+ props: {
57
+ modelValue: {
58
+ type: [String, Array],
59
+ },
60
+ },
61
+ mixins: [InputMixin],
62
+ computed: {
63
+ lazy() {
64
+ switch (this.type) {
65
+ case 'integer':
66
+ case 'number':
67
+ case 'decimal':
68
+ case 'float':
69
+ case 'url':
70
+ case 'key':
71
+ return true;
72
+ break;
73
+ }
74
+ },
75
+ actualPlaceholder() {
76
+ if (this.field.placeholder) {
77
+ return this.field.placeholder;
78
+ }
79
+
80
+ switch (this.type) {
81
+ case 'integer':
82
+ case 'number':
83
+ case 'decimal':
84
+ case 'float':
85
+ return '0';
86
+ break;
87
+ case 'url':
88
+ return 'https://www.website.com'
89
+ break;
90
+ case 'email':
91
+ return 'you@youremail.com'
92
+ break;
93
+ }
94
+ },
95
+ addLabel() {
96
+ if (this.numValues) {
97
+ return `Add another option`
98
+
99
+ } else {
100
+ return `Add options`
101
+ }
102
+ },
103
+ },
104
+ methods: {
105
+ titleBlurred(index) {
106
+ var entry;
107
+ if (this.multiValue) {
108
+ entry = this.model[index];
109
+ } else {
110
+ entry = this.model;
111
+ }
112
+
113
+ //Leave it pristine
114
+ if(entry.title === entry.value) {
115
+ return;
116
+ }
117
+
118
+ //Clear it out
119
+ delete entry.pristine;
120
+ },
121
+ valueTouched(index) {
122
+ var entry;
123
+ if (this.multiValue) {
124
+ entry = this.model[index];
125
+ } else {
126
+ entry = this.model;
127
+ }
128
+
129
+ ///////////////
130
+
131
+ delete entry.pristine;
132
+
133
+ ///////////////
134
+
135
+ this.touch();
136
+ },
137
+ entryTitleChanged(index) {
138
+
139
+ //Need this as otherwise the v-model will beat it
140
+ this.$nextTick(function() {
141
+ var entry;
142
+ if (this.multiValue) {
143
+ entry = this.model[index];
144
+ } else {
145
+ entry = this.model;
146
+ }
147
+
148
+ if (entry.pristine) {
149
+ entry.value = entry.title;
150
+ }
151
+
152
+ })
153
+ },
154
+ cleanOutputValue(obj) {
155
+ obj.value = this.cleanTextInput(obj.value, this.type, this);
156
+ return obj;
157
+ },
158
+ cleanInputValue(obj) {
159
+ obj.value = this.cleanTextInput(obj.value, this.type, this);
160
+ return obj;
161
+ },
162
+ getNewDefaultEntry() {
163
+ return {
164
+ pristine:true,
165
+ };
166
+ },
167
+ },
168
+ }
169
+ </script>
170
+ <style lang="scss" scoped>
171
+ .ux-text-row {
172
+ margin-bottom: 0.5em;
173
+ }
174
+
175
+ input {
176
+ border-radius: 0.1em;
177
+ padding: 0.5em;
178
+ box-sizing: border-box;
179
+ background: rgba(#000, 0.05);
180
+ border: 1px solid rgba(#000, 0.1);
181
+ font-size: inherit;
182
+ appearance: none;
183
+ line-height: 1;
184
+ height: 2.5em;
185
+
186
+ &:focus {
187
+ background: none;
188
+ border: 1px solid $primary;
189
+ outline: none;
190
+ }
191
+ }
192
+
193
+
194
+ .ux-text-wrap {
195
+ position: relative;
196
+
197
+ &.prefixed {
198
+ input {
199
+ padding-left: 1.8em;
200
+ }
201
+ }
202
+
203
+ .ux-text-prefix {
204
+ position: absolute;
205
+ left: 0;
206
+ top: 0;
207
+ bottom: 0;
208
+ width: 3.1em;
209
+ pointer-events: none;
210
+ display: flex;
211
+ align-items: center;
212
+ padding: 0.5em 0;
213
+ justify-content: right;
214
+ opacity: 0.5;
215
+ font-size:0.5em;
216
+ }
217
+
218
+ .ux-text-suffix {
219
+ position: absolute;
220
+ top: 0;
221
+ bottom: 0;
222
+ right: 0;
223
+ pointer-events: none;
224
+ display: flex;
225
+ align-items: center;
226
+ padding: 0.5em;
227
+ opacity: 0.5;
228
+ }
229
+ }
230
+
231
+ .ux-text-row .ux-btn {
232
+ margin-left: 0.25em;
233
+ }
234
+
235
+ .ux-text-input-multiple {
236
+ width: 100%;
237
+ }
238
+
239
+ .ux-text-input-single {
240
+ width: 100%;
241
+ // width: calc(100% - 0.5em);
242
+ // margin: 0 0.5em 0 0;
243
+ }
244
+ </style>
@@ -0,0 +1,235 @@
1
+ <template>
2
+ <label class="ux-field-title" v-if="showLabel">{{label}} <span class="ux-required-marker" v-if="required">*</span></label>
3
+ <div class="ux-field-description" v-if="showDescription">{{description}}</div>
4
+ <div v-if="multiValue">
5
+ <flex-row wrap class="ux-text-row" :key="index" v-for="(entry, index) in model">
6
+ <flex-cell shrink>
7
+ <native-select v-model="entry.countryCode" :field="countryCodeField">
8
+ <template v-if="!entry.countryCode">
9
+ Country Code
10
+ </template>
11
+ <template v-else>
12
+ <div v-if="lookup[entry.countryCode]">{{lookup[entry.countryCode].emoji}} - {{lookup[entry.countryCode].alpha2}}</div>
13
+ <div v-else>Loading</div>
14
+ </template>
15
+ </native-select>
16
+ <!-- <select class="ux-country-code-select" v-model="entry.countryCode">
17
+ <option value="">None</option>
18
+ <option :value="country.value" v-for="country in countryCodes">{{country.title}}</option>
19
+ </select> -->
20
+ </flex-cell>
21
+ <flex-cell>
22
+ <div class="ux-text-wrap">
23
+ <input class="ux-field-focus ux-text-input-multiple" :placeholder="actualPlaceholder" @focus="touch" ref="input" @keydown.enter.stop.prevent="add()" v-model="entry.number" />
24
+ </div>
25
+ </flex-cell>
26
+ <flex-cell shrink vcenter>
27
+ <ux-button tag="a" icon v-if="canRemoveValue" @click="remove(entry)">
28
+ <ux-icon icon="fa-times" />
29
+ </ux-button>
30
+ </flex-cell>
31
+ </flex-row>
32
+ <ux-button v-if="canAddValue" @click="add()">{{addLabel}}
33
+ <ux-icon icon="fa-plus" right />
34
+ </ux-button>
35
+ </div>
36
+ <template v-else>
37
+ <flex-row wrap class="ux-text-row" :key="index">
38
+ <flex-cell shrink>
39
+ <select class="ux-country-code-select" v-model="model.countryCode">
40
+ <option value="">None</option>
41
+ <option :value="country.value" v-for="country in countryCodes">{{country.title}}</option>
42
+ </select>
43
+ </flex-cell>
44
+ <flex-cell>
45
+ <div class="ux-text-wrap">
46
+ <input class="ux-field-focus ux-text-input-multiple" :placeholder="actualPlaceholder" @focus="touch" ref="input" @keydown.enter.stop.prevent="add()" v-model="model.number" />
47
+ </div>
48
+ </flex-cell>
49
+ </flex-row>
50
+ </template>
51
+ </template>
52
+ <script>
53
+ import InputMixin from './input-mixin';
54
+ import NativeSelect from './select.vue';
55
+
56
+
57
+
58
+ export default {
59
+ props: {
60
+ modelValue: {
61
+ type: [Object, Array],
62
+ },
63
+ },
64
+ components: {
65
+ NativeSelect,
66
+ },
67
+ mixins: [InputMixin],
68
+ async created() {
69
+ var countries = await this.$qik.system.countries();
70
+
71
+ var mapped = countries.reduce(function(set, country) {
72
+
73
+ if (!country.countryCallingCodes || !country.countryCallingCodes.length) {
74
+ return set;
75
+ }
76
+
77
+ ////////////////////////////////////
78
+
79
+ var title = `${country.name} (${country.countryCallingCodes.join(', ')})`;
80
+ var matchingKey = title;
81
+
82
+ if (set.values[matchingKey]) {
83
+ return set;
84
+ }
85
+
86
+ set.values[matchingKey] = true;
87
+
88
+ set.lookup[country.alpha2] = country;
89
+
90
+ set.value.push({
91
+ title: `${title} - ${country.emoji}`,
92
+ value: country.alpha2,
93
+ emoji: country.emoji,
94
+ })
95
+
96
+ return set;
97
+ }, {
98
+ value: [],
99
+ values: {},
100
+ lookup: {},
101
+ })
102
+
103
+ this.countryCodes = mapped.value;
104
+ this.lookup = mapped.lookup;
105
+
106
+ },
107
+ data() {
108
+ return {
109
+ countryCodes: [],
110
+ lookup: {},
111
+ }
112
+ },
113
+ computed: {
114
+
115
+ actualPlaceholder() {
116
+ return `+61 000 000 000`;
117
+ },
118
+ countryCodeField() {
119
+ return {
120
+ minimum: 0,
121
+ maximum: 1,
122
+ type: 'string',
123
+ options: this.countryCodes,
124
+
125
+ }
126
+ },
127
+ defaultCountryCode() {
128
+
129
+ var userCountryCode = this.user ? this.user.countryCode : '';
130
+ var orgCountryCode = this.user && this.user.organisation ? this.user.organisation.countryCode : '';
131
+
132
+
133
+ return userCountryCode || orgCountryCode;
134
+ },
135
+ },
136
+ methods: {
137
+
138
+ // cleanOutputValue(v) {
139
+ // var cleaned = cleanInput(v, this.type, this);
140
+ // return cleaned ? String(cleaned) : '';
141
+ // },
142
+ // cleanInputValue(v) {
143
+ // return cleanInput(v, this.type, this);
144
+ // },
145
+ getNewDefaultEntry() {
146
+ return {
147
+ label: '',
148
+ countryCode: this.defaultCountryCode,
149
+ number: '',
150
+ };
151
+ },
152
+ },
153
+ }
154
+ </script>
155
+ <style lang="scss" scoped>
156
+ .ux-text-row {
157
+ margin-bottom: 0.5em;
158
+ }
159
+
160
+
161
+ // select,
162
+ input {
163
+ border-radius: 0.1em;
164
+ padding: 0.5em;
165
+ box-sizing: border-box;
166
+ background: rgba(#000, 0.05);
167
+ border: 1px solid rgba(#000, 0.1);
168
+ font-size: inherit;
169
+ appearance: none;
170
+ line-height: 1;
171
+ height: 2.5em;
172
+
173
+ &:focus {
174
+ background: none;
175
+ border: 1px solid $primary;
176
+ outline: none;
177
+ }
178
+ }
179
+
180
+
181
+ // select {
182
+ // max-width: clamp(30px, 5em, 100px);
183
+ // }
184
+
185
+
186
+ .ux-text-wrap {
187
+ position: relative;
188
+
189
+ &.prefixed {
190
+ input {
191
+ padding-left: 1.2em;
192
+ }
193
+ }
194
+
195
+ .ux-text-prefix {
196
+ position: absolute;
197
+ left: 0;
198
+ top: 0;
199
+ bottom: 0;
200
+ width: 1em;
201
+ pointer-events: none;
202
+ display: flex;
203
+ align-items: center;
204
+ padding: 0.5em 0;
205
+ justify-content: right;
206
+ opacity: 0.5;
207
+ }
208
+
209
+ .ux-text-suffix {
210
+ position: absolute;
211
+ top: 0;
212
+ bottom: 0;
213
+ right: 0;
214
+ pointer-events: none;
215
+ display: flex;
216
+ align-items: center;
217
+ padding: 0.5em;
218
+ opacity: 0.5;
219
+ }
220
+ }
221
+
222
+ .ux-text-row .ux-btn {
223
+ margin-left: 0.25em;
224
+ }
225
+
226
+ .ux-text-input-multiple {
227
+ width: 100%;
228
+ }
229
+
230
+ .ux-text-input-single {
231
+ width: 100%;
232
+ // width: calc(100% - 0.5em);
233
+ // margin: 0 0.5em 0 0;
234
+ }
235
+ </style>
@@ -0,0 +1,117 @@
1
+ <template>
2
+ <div class="search">
3
+ <input v-model="model" :placeholder="placeholder" />
4
+ <div @click="clear()" class="icon">
5
+ <ux-icon :icon="icon"/>
6
+
7
+ </div>
8
+
9
+ </div>
10
+ </template>
11
+ <script>
12
+ import _debounce from 'lodash/debounce';
13
+
14
+
15
+
16
+
17
+ export default {
18
+ props: {
19
+ modelValue: {
20
+ type: String,
21
+ },
22
+ placeholder: {
23
+ type: String,
24
+ },
25
+ debounce: {
26
+ type: Number,
27
+ default () {
28
+ return 500;
29
+ }
30
+ },
31
+ loading:{
32
+ type:Boolean,
33
+ }
34
+ },
35
+ mounted() {
36
+ var self = this;
37
+ self.dispatch = _debounce(() => {
38
+ self.$emit('update:modelValue', self.value);
39
+ }, self.debounce)
40
+ },
41
+ data() {
42
+ return {
43
+ value: String(this.modelValue),
44
+ }
45
+ },
46
+ methods:{
47
+ clear() {
48
+ this.value = '';
49
+ this.$emit('update:modelValue', this.value);
50
+ }
51
+ },
52
+ watch: {
53
+ modelValue(val, old) {
54
+ this.value = val;
55
+ },
56
+ },
57
+ computed: {
58
+ icon() {
59
+ return this.loading ? `fa-spinner fa-spin` : this.value ? `fa-times` : `fa-search`;
60
+ },
61
+ selectableOptions() {
62
+ return this.options.map(function(option) {
63
+ const value = option.value || option;
64
+ const label = option.title || option.name || option.label || value;
65
+
66
+ return {
67
+ label,
68
+ value,
69
+ }
70
+ })
71
+ },
72
+ model: {
73
+ get() {
74
+ return this.value;
75
+ },
76
+ set(value) {
77
+ this.value = value;
78
+ this.dispatch();
79
+ }
80
+ }
81
+ }
82
+ }
83
+ </script>
84
+ <style lang="scss" scoped>
85
+ .search {
86
+ position: relative;
87
+ width:100%;
88
+
89
+ & > .icon {
90
+ position:absolute;
91
+ right:0;
92
+ top:0;
93
+ bottom:0;
94
+ line-height: 2.5em;
95
+ padding:0 1em;
96
+ }
97
+
98
+ input {
99
+ width:100%;
100
+ border-radius: 5em;
101
+ padding: 0.5em 1em;
102
+ box-sizing: border-box;
103
+ background: rgba(#000, 0.05);
104
+ border: 1px solid transparent;
105
+ font-size: inherit;
106
+ appearance: none;
107
+ line-height: 1;
108
+ // height: 2.5em;
109
+
110
+ &:focus {
111
+ background: none;
112
+ border: 1px solid $primary;
113
+ outline: none;
114
+ }
115
+ }
116
+ }
117
+ </style>