renusify 1.2.0 → 1.2.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.
@@ -33,6 +33,7 @@
33
33
  max-width: 100%;
34
34
  width: 100%;
35
35
  position: relative;
36
+ overflow-x: hidden;
36
37
  }
37
38
 
38
39
  }
@@ -81,7 +81,7 @@
81
81
  .toolbar-items {
82
82
  display: flex;
83
83
  height: inherit;
84
- align-items: flex-end;
84
+ align-items: center;
85
85
  }
86
86
 
87
87
  .menu-title {
@@ -8,35 +8,35 @@
8
8
  </div>
9
9
  <transition name="scale" v-if="!hideBottomBtn">
10
10
  <r-btn v-if="!is_bottom" fab class="go-buttom" :label="newMsg.num" @click.prevent="goTo(newMsg.first_id)">
11
- <r-icon v-html="$r.icons.chevron_left"></r-icon>
11
+ <r-icon v-html="$r.icons.chevron_down"></r-icon>
12
12
  </r-btn>
13
13
  </transition>
14
14
  </template>
15
15
 
16
16
  <script>
17
- import RChatMsg from "./chatMsg";
17
+ import RChatMsg from "./chatMsg";
18
18
 
19
- export default {
20
- components: {
21
- RChatMsg
22
- },
23
- emits: ["see"],
24
- props: {
25
- newMsg: {
26
- type: Object, default: () => {
27
- return {
28
- num: 0,
29
- first_id: null
30
- }
31
- }
32
- },
33
- hideBottomBtn: Boolean,
34
- messages: {type: Array,},
35
- myself: {type: Object},
36
- participants: {type: Object}
37
- },
38
- data() {
39
- return {
19
+ export default {
20
+ components: {
21
+ RChatMsg
22
+ },
23
+ emits: ["see"],
24
+ props: {
25
+ newMsg: {
26
+ type: Object, default: () => {
27
+ return {
28
+ num: 0,
29
+ first_id: null
30
+ }
31
+ }
32
+ },
33
+ hideBottomBtn: Boolean,
34
+ messages: {type: Array,},
35
+ myself: {type: Object},
36
+ participants: {type: Object}
37
+ },
38
+ data() {
39
+ return {
40
40
  timeout_id: null,
41
41
  timeout_scroll: null,
42
42
  is_bottom: false
@@ -10,74 +10,17 @@ export default {
10
10
  template: String,
11
11
  script: String
12
12
  },
13
- methods: {
14
- strToObj2(str) {
15
- str = str.trim()
16
- str = this.$helper.replacer(str, "\n", '')
17
- str = this.$helper.replacer(str, "\r", '')
18
- let open = 0
19
- let pre = 0
20
- let to = 0
21
- let items = []
22
- const s = str
23
- s.split('').forEach((c, i) => {
24
- to++
25
- if (c === '{') {
26
- open++
27
- }
28
- if (c === '}') {
29
- open--
30
- }
31
- if (open === 0 && c === ',') {
32
- items.push(str.substr(pre, to - 1))
33
- pre = i + 1
34
- to = 0
35
- }
36
- })
37
- items.push(str.substr(pre, str.length))
38
- return items
39
- },
40
- strToObj(str) {
41
- str = str.trim()
42
- if (str.substr(0, 1) === '{') {
43
- str = str.substr(1, str.length - 2)
44
- }
45
- let res = {}
46
- this.strToObj2(str).forEach((item) => {
47
- item = item.trim()
48
- const f = item.indexOf('(')
49
- const o = item.indexOf(':')
50
-
51
- if ((f > 0 && f < o) || o < 0) {
52
- res[item.substr(0, f)] = eval("(" + 'function ' + item + ")");
53
- } else {
54
-
55
- item = [item.substr(0, o).trim(), item.substr(o, item.length).replace(':', '').trim()]
56
- if (item[1].substr(0, 1) === '{') {
57
- res[item[0]] = this.strToObj(item[1])
58
- } else {
59
- res[item[0]] = eval(item[1])
60
- }
61
-
62
- }
63
- })
64
-
65
- return res
66
- },
67
- },
68
13
  computed: {
69
14
  page() {
70
15
  let temp = this.template || ''
71
16
  let scr = this.script || 'name:"test"'
72
-
73
17
  scr = this.$helper.replacer(scr, '&lt;', '<')
74
18
  scr = this.$helper.replacer(scr, '&gt;', '>')
75
-
76
19
  return defineAsyncComponent(() =>
77
20
  new Promise((resolve, reject) => {
78
21
  resolve({
79
22
  template: temp,
80
- ...this.strToObj(scr)
23
+ ...eval('Object({' + scr + '})')
81
24
  })
82
25
  }))
83
26
  }
@@ -1,7 +1,6 @@
1
1
  @import "renusify/style/_include.scss";
2
2
 
3
3
  .#{$prefix}container {
4
- max-width: 100%;
5
4
  padding: $container-padding-x;
6
5
  margin-right: auto;
7
6
  margin-left: auto;
@@ -9,7 +8,8 @@
9
8
  @include make-container-max-widths;
10
9
 
11
10
  &.container-fluid {
12
- max-width: 100%
11
+ max-width: 100%;
12
+ width: 100%;
13
13
  }
14
14
 
15
15
  &.fill {
@@ -0,0 +1,321 @@
1
+ <template>
2
+ <div :class="$r.prefix + 'cropper'">
3
+ <div v-if="show && allWPH.length > 1">
4
+ <r-chip
5
+ v-for="(item, i) in allWPH"
6
+ :key="i"
7
+ :class="{ 'color-one-text': item === currentWPH }"
8
+ class="me-1 mb-1 cursor-pointer"
9
+ label
10
+ outlined
11
+ @click.prevent="changeWPH(item)"
12
+ >{{ item }}
13
+ </r-chip
14
+ >
15
+ </div>
16
+ <div
17
+ v-if="show"
18
+ ref="imageBox"
19
+ v-touch="{
20
+ start: moveStart,
21
+ move: setPosition,
22
+ }"
23
+ class="image-box"
24
+ >
25
+ <div ref="thumbBox" class="thumb-box"></div>
26
+ </div>
27
+ <div class="action d-flex">
28
+ <template v-if="show">
29
+ <r-btn @click="getCrop">{{ $t("crop", "renusify") }}</r-btn>
30
+ <r-spacer></r-spacer>
31
+ <r-btn icon tile @click.prevent="zoomIn()">+</r-btn>
32
+ <r-btn icon tile @click.prevent="zoomOut()">-</r-btn>
33
+ </template>
34
+
35
+ <input
36
+ v-if="!show && selectImg"
37
+ ref="file"
38
+ accept="image/*"
39
+ type="file"
40
+ @change="select_img"
41
+ />
42
+ </div>
43
+ <img
44
+ v-if="showCropped && cropped"
45
+ :height="height"
46
+ :src="cropped"
47
+ :width="width"
48
+ alt="cropped"
49
+ />
50
+ </div>
51
+ </template>
52
+
53
+ <script>
54
+ export default {
55
+ name: "r-cropper",
56
+ props: {
57
+ wPH: {type: [Number, String, Array], default: 1},
58
+ imgSrc: [String, Blob],
59
+ maxWidth: Number,
60
+ showCropped: Boolean,
61
+ getBlob: Boolean,
62
+ selectImg: {type: Boolean, default: true},
63
+ },
64
+ data() {
65
+ return {
66
+ show: false,
67
+ cropped: null,
68
+ width: null,
69
+ height: null,
70
+ state: {},
71
+ ratio: 1,
72
+ image: new Image(),
73
+ currentWPH: null,
74
+ };
75
+ },
76
+ mounted() {
77
+ this.width = 280;
78
+ this.height = 280;
79
+ this.currentWPH = this.allWPH[0];
80
+ if (this.currentWPH > 1) {
81
+ this.height = this.width / this.currentWPH;
82
+ } else {
83
+ this.width = this.height * this.currentWPH;
84
+ }
85
+ if (typeof this.imgSrc === "string") {
86
+ this.crop(this.imgSrc);
87
+ } else {
88
+ const that = this;
89
+ const reader = new FileReader();
90
+ reader.onload = function (e) {
91
+ that.crop(e.target.result);
92
+ };
93
+
94
+ reader.readAsDataURL(this.imgSrc);
95
+ }
96
+ },
97
+ computed: {
98
+ allWPH() {
99
+ const is_array = Array.isArray(this.wPH);
100
+ let wPH = [];
101
+ if (is_array) {
102
+ this.wPH.forEach((item) => {
103
+ item = item.toString().split("/");
104
+ if (item.length === 2) {
105
+ wPH.push(
106
+ parseFloat((parseFloat(item[0]) / parseFloat(item[1])).toFixed(4))
107
+ );
108
+ } else {
109
+ wPH.push(parseFloat(parseFloat(item[0]).toFixed(4)));
110
+ }
111
+ });
112
+ } else {
113
+ if (typeof this.wPH === "string") {
114
+ let item = this.wPH;
115
+ item = item.split("/");
116
+ if (item.length === 2) {
117
+ wPH.push(
118
+ parseFloat((parseFloat(item[0]) / parseFloat(item[1])).toFixed(4))
119
+ );
120
+ } else {
121
+ wPH.push(parseFloat(parseFloat(item[0]).toFixed(4)));
122
+ }
123
+ } else {
124
+ wPH.push(parseFloat(parseFloat(this.wPH).toFixed(4)));
125
+ }
126
+ }
127
+ return wPH;
128
+ },
129
+ },
130
+ methods: {
131
+ getCrop() {
132
+ this.cropped = this.getDataURL();
133
+ if (this.getBlob) {
134
+ this.$emit("cropped", this.get_blob());
135
+ }
136
+
137
+ this.show = false;
138
+ },
139
+ changeWPH(e) {
140
+ this.currentWPH = e;
141
+ this.width = 280;
142
+ this.height = 280;
143
+ if (this.currentWPH > 1) {
144
+ this.height = this.width / this.currentWPH;
145
+ } else {
146
+ this.width = this.height * this.currentWPH;
147
+ }
148
+ let thumbBox = this.$refs.thumbBox;
149
+ thumbBox.style.width = this.width + "px";
150
+ thumbBox.style.height = this.height + "px";
151
+ },
152
+ moveStart() {
153
+ let el = this.$refs.imageBox;
154
+ let bg = el.style.backgroundPosition.split(" ");
155
+ this.state.x = parseInt(bg[0]);
156
+ this.state.y = parseInt(bg[1]);
157
+ },
158
+ setPosition(e) {
159
+ let el = this.$refs.imageBox;
160
+
161
+ let bgX = e.goX + this.state.x;
162
+ let bgY = e.goY + this.state.y;
163
+
164
+ el.style.backgroundPosition = bgX + "px " + bgY + "px";
165
+ },
166
+ select_img(e) {
167
+ const that = this;
168
+ const reader = new FileReader();
169
+ reader.onload = function (e) {
170
+ that.crop(e.target.result);
171
+ };
172
+
173
+ this.$emit("orginal", e.target.files[0]);
174
+ reader.readAsDataURL(e.target.files[0]);
175
+ },
176
+ getDataURL() {
177
+ let el = this.$refs.imageBox;
178
+ let thumbBox = this.$refs.thumbBox;
179
+ let width = thumbBox.clientWidth,
180
+ height = thumbBox.clientHeight,
181
+ canvas = document.createElement("canvas"),
182
+ dim = el.style.backgroundPosition.split(" "),
183
+ size = el.style.backgroundSize.split(" "),
184
+ dx = parseInt(dim[0]) - el.clientWidth / 2 + width / 2 + 1,
185
+ dy = parseInt(dim[1]) - el.clientHeight / 2 + height / 2 + 1,
186
+ dw = parseInt(size[0]),
187
+ dh = parseInt(size[1]),
188
+ sh = parseInt(this.image.height),
189
+ sw = parseInt(this.image.width);
190
+
191
+ let n = 0;
192
+ if (dw >= 280) {
193
+ n = Math.abs((sw / dw) * dx * 2);
194
+ }
195
+ canvas.width = sw - n;
196
+ if (this.maxWidth && canvas.width > this.maxWidth) {
197
+ canvas.width = this.maxWidth;
198
+ }
199
+ canvas.height = canvas.width / this.currentWPH;
200
+ while (
201
+ parseFloat((canvas.width / canvas.height).toFixed(4)) !==
202
+ this.currentWPH
203
+ ) {
204
+ canvas.width -= 1;
205
+ canvas.height = canvas.width / this.currentWPH;
206
+ if (canvas.width < 1) {
207
+ break;
208
+ }
209
+ }
210
+ let context = canvas.getContext("2d");
211
+ context.drawImage(
212
+ this.image,
213
+ 0,
214
+ 0,
215
+ sw,
216
+ sh,
217
+ (dx * canvas.width) / width,
218
+ (dy * canvas.height) / height,
219
+ (dw * canvas.width) / width,
220
+ (dh * canvas.height) / height
221
+ );
222
+ return canvas.toDataURL("image/png");
223
+ },
224
+ get_blob() {
225
+ let imageData = this.getDataURL();
226
+ let b64 = imageData.replace("data:image/png;base64,", "");
227
+ let binary = atob(b64);
228
+ let array = [];
229
+ for (let i = 0; i < binary.length; i++) {
230
+ array.push(binary.charCodeAt(i));
231
+ }
232
+ let b = new Blob([new Uint8Array(array)], {
233
+ type: "image/png",
234
+ });
235
+ b.name = "file.png";
236
+ return b;
237
+ },
238
+ zoomIn() {
239
+ this.ratio += 0.01;
240
+ this.setBackground();
241
+ },
242
+ zoomOut() {
243
+ this.ratio -= 0.01;
244
+ this.setBackground();
245
+ },
246
+ setBackground() {
247
+ let el = this.$refs.imageBox;
248
+ let w = parseInt(this.image.width) * this.ratio;
249
+ let h = parseInt(this.image.height) * this.ratio;
250
+
251
+ let pw = (el.clientWidth - w) / 2;
252
+ let ph = (el.clientHeight - h) / 2;
253
+
254
+ el.setAttribute(
255
+ "style",
256
+ "background-image: url(" +
257
+ this.image.src +
258
+ "); " +
259
+ "background-size: " +
260
+ w +
261
+ "px " +
262
+ h +
263
+ "px; " +
264
+ "background-position: " +
265
+ pw +
266
+ "px " +
267
+ ph +
268
+ "px; " +
269
+ "background-repeat: no-repeat"
270
+ );
271
+ },
272
+ crop(img) {
273
+ this.show = true;
274
+ this.cropped = null;
275
+ setTimeout(() => {
276
+ const that = this;
277
+ let thumbBox = this.$refs.thumbBox;
278
+ thumbBox.style.width = this.width + "px";
279
+ thumbBox.style.height = this.height + "px";
280
+
281
+ this.image.onload = function () {
282
+ that.ratio = parseInt(that.width) / that.image.width;
283
+ that.setBackground();
284
+ };
285
+ this.image.src = img;
286
+ }, 100);
287
+ },
288
+ },
289
+ };
290
+ </script>
291
+
292
+ <style lang="scss">
293
+ @import "~renusify/style/include";
294
+
295
+ .#{$prefix}cropper {
296
+ width: 300px;
297
+
298
+ .image-box {
299
+ position: relative;
300
+ height: 300px;
301
+ width: 300px;
302
+ border: 1px solid #aaa;
303
+ overflow: hidden;
304
+ background: #fff no-repeat;
305
+ cursor: move;
306
+ }
307
+
308
+ .thumb-box {
309
+ position: absolute;
310
+ top: 50%;
311
+ left: 50%;
312
+ width: 200px;
313
+ height: 200px;
314
+ transform: translate(-50%, -50%);
315
+ box-sizing: border-box;
316
+ border: 1px solid rgb(130, 130, 130);
317
+ box-shadow: 0 0 0 2000px rgba(0, 0, 0, 0.4);
318
+ background: none repeat scroll 0 0 transparent;
319
+ }
320
+ }
321
+ </style>
@@ -15,7 +15,11 @@ export default {
15
15
  addFile() {
16
16
  this.CancelTokenSource = this.$axios.CancelToken.source()
17
17
  this.file = this.$refs.file.files[0]
18
- this.checkSave()
18
+ if (!this.wPH) {
19
+ this.checkSave()
20
+ } else {
21
+ this.showCrop = true
22
+ }
19
23
  this.showAdd = false
20
24
  },
21
25
  pickFile() {
@@ -97,7 +101,7 @@ export default {
97
101
  saveImage() {
98
102
  this.imageStatus = 'inProgress'
99
103
  let fileData = new FormData()
100
- fileData.append('file', this.file)
104
+ fileData.append('file', this.file, this.file.name)
101
105
  let headers = this.headers
102
106
  if (!headers) {
103
107
  headers = {}
@@ -140,4 +144,4 @@ export default {
140
144
  )
141
145
  },
142
146
  }
143
- }
147
+ }
@@ -14,7 +14,7 @@
14
14
  >
15
15
  {{ `% ${uploadPercentage}` }}
16
16
  </r-progress-circle>
17
- <r-btn :href="'/'+fileLink" class="image-copy" icon>
17
+ <r-btn :href="'/'+fileLink" class="image-copy" icon target="_blank">
18
18
  <r-icon v-html="$r.icons.eye"></r-icon>
19
19
  </r-btn>
20
20
  <img v-if="isImg()" :class="`image ${imageStatus} `" :src="getUrl(file)">
@@ -40,6 +40,12 @@
40
40
  type="file"
41
41
  v-if="showFile"
42
42
  >
43
+ <r-modal v-model="showCrop" maxWidth="300px">
44
+ <r-card>
45
+ <r-cropper v-if="wPH&&file" :imgSrc="file" :selectImg="false" :w-p-h="wPH" get-blob showCropped
46
+ @cropped="file=$event,checkSave()"></r-cropper>
47
+ </r-card>
48
+ </r-modal>
43
49
  </div>
44
50
  </template>
45
51
 
@@ -70,11 +76,12 @@ export default {
70
76
  },
71
77
  metaRequired: Boolean
72
78
  },
73
- emits:['file-link','select'],
79
+ emits: ['file-link', 'select'],
74
80
  data() {
75
81
  return {
76
82
  showAdd: true,
77
83
  showFile: true,
84
+ showCrop: false,
78
85
  metaList: {}
79
86
  }
80
87
  },
@@ -1,11 +1,12 @@
1
1
  <template>
2
2
  <r-card v-if="!autoSend" class="overflow-auto">
3
3
  <r-container>
4
- <h1 class="title">{{ title }}</h1>
4
+ <h1 v-if="title" class="title">{{ title }}</h1>
5
5
  <r-form ref="form" v-model="valid" @submit.prevent="save">
6
6
  <r-row>
7
7
  <template :key="key" v-for="(item,key) in options">
8
- <r-col class="col-12" v-if="item['formInput']!==false&&iff(options[key])">
8
+ <r-col v-if="item['formInput']!==false&&iff(options[key])"
9
+ :class="options[key]['r-col']?options[key]['r-col']:'col-12'">
9
10
  <component
10
11
  :is="'r-'+item['type']"
11
12
  :label="$t(key)"
@@ -147,7 +148,7 @@ export default {
147
148
  getAttr(data) {
148
149
  let res = {}
149
150
  for (let i in data) {
150
- if (this.$helper.hasKey(data, i) && !['formInput', 'sortable', 'type', 'tableShow', 'priority', '$if'].includes(i)) {
151
+ if (this.$helper.hasKey(data, i) && !['formInput', 'sortable', 'type', 'tableShow', 'priority', '$if', 'r-col'].includes(i)) {
151
152
  if (i === '$bind') {
152
153
  data[i].forEach((item) => {
153
154
  res[item[0]] = this.editedItem[item[1]]
@@ -22,6 +22,7 @@
22
22
  </template>
23
23
  <script>
24
24
  import SvgImg from "./svgImg";
25
+
25
26
  export default {
26
27
  name: 'r-img',
27
28
  components: {SvgImg},
@@ -83,7 +84,7 @@ export default {
83
84
  return null
84
85
  }
85
86
  let res = this.src
86
- if (!(res.startsWith('/') || res.startsWith('http://') || res.startsWith('https://'))) {
87
+ if (!(res.startsWith('/') || res.startsWith('http://') || res.startsWith('https://') || res.startsWith('data:image/'))) {
87
88
  res = '/' + res
88
89
  }
89
90
  if (this.src.search('[?]') === -1) {
@@ -42,6 +42,7 @@ const list = {
42
42
  'r-divider': {'p': 'container/divider.vue', 'c': [], 'd': []},
43
43
  'r-content': {'p': 'content/index.vue', 'c': [], 'd': []},
44
44
  'r-count-down': {'p': 'countdown/index.vue', 'c': [], 'd': []},
45
+ 'r-cropper': {'p': 'cropper/index.vue', 'c': [], 'd': ['touch']},
45
46
  'r-float': {'p': 'float/index.vue', 'c': [], 'd': []},
46
47
  'r-form': {'p': 'form/form.vue', 'c': [], 'd': []},
47
48
  'r-color-input': {
@@ -56,7 +57,7 @@ const list = {
56
57
  },
57
58
  'r-file-input': {
58
59
  'p': 'form/fileUploader/index.vue',
59
- 'c': ['r-input', 'r-row', 'r-btn', 'r-icon', 'r-progress-circle', 'r-text-input'],
60
+ 'c': ['r-input', 'r-row', 'r-btn', 'r-icon', 'r-progress-circle', 'r-text-input', 'r-cropper'],
60
61
  'd': []
61
62
  },
62
63
  'r-tel-input': {
@@ -141,7 +142,7 @@ const list = {
141
142
  'r-progress-line': {'p': 'progress/line.vue', 'c': [], 'd': []},
142
143
  'r-search-box': {
143
144
  'p': 'searchBox/index.vue',
144
- 'c': ['r-input', 'r-progress-line', 'r-card', 'r-list', 'r-btn', 'r-icon'],
145
+ 'c': ['r-progress-line', 'r-card', 'r-list', 'r-btn', 'r-icon', 'r-select'],
145
146
  'd': ['click-outside']
146
147
  },
147
148
  'r-skeleton': {'p': 'skeleton/index.vue', 'c': [], 'd': []},
@@ -1,75 +1,99 @@
1
1
  <template>
2
- <div :class="$r.prefix+'search-container'">
3
- <template v-if="!closable||show">
4
- <div class="search-input" v-click-outside="handleclose">
5
- <r-input :active="active"
6
- :icon="$r.icons.search"
7
- @icon="show=false"
8
- v-bind="$attrs"
9
- :class="{'z-important':open}"
10
- :modelValue="lazyValue"
11
- :input-control-class="[inputControlClass,{'search-active-border':open}]">
12
- <input :autofocus="autofocus"
13
- type="text"
14
- @focusin="active=true"
15
- @focusout="active=false"
16
- @input="handle"
17
- :value="lazyValue"
18
- autocomplete="no"
19
- />
20
- <r-progress-line v-if="loading" color="color-two" class="w-full"></r-progress-line>
21
- </r-input>
22
- <r-card v-if="open&&(list.length>0||!loading)"
23
- class="card-select z-important"
24
- :class="{'card-tile':$attrs.tile!==undefined&&$attrs.tile!==false,'to-top':openToTop}"
2
+ <div :class="$r.prefix + 'search-box'">
3
+ <template v-if="!closable || show">
4
+ <div
5
+ v-click-outside="handleclose"
6
+ :class="[inputClass, { 'z-important search-open': open }]"
7
+ class="search-input"
8
+ >
9
+ <span v-if="categories" class="w-30">
10
+ <r-select-input
11
+ v-model="category"
12
+ :items="categories"
13
+ class="mt-0"
14
+ disable-search
15
+ first-select
16
+ hide
17
+ justValue
18
+ ></r-select-input>
19
+ </span>
20
+ <span
21
+ :class="{ 'w-70': categories, 'w-full': !categories }"
22
+ class="d-flex v-center"
25
23
  >
26
- <r-list v-if="list.length>0"
27
- :items="list"
28
- @update:modelValue="listInput">
29
- <template v-slot="props">
30
- <slot :item="props.item">
31
- <div class="list-title">{{ props.item['name'] }}</div>
32
- </slot>
33
- </template>
34
- </r-list>
35
- <div v-else-if="!loading" class="py-5">
36
- {{ notFoundMsg }}
37
- </div>
38
- </r-card>
24
+ <input
25
+ :placeholder="label"
26
+ :value="lazyValue"
27
+ autocomplete="no"
28
+ class="flex-grow-1"
29
+ type="text"
30
+ @focusin="active = true"
31
+ @focusout="active = false"
32
+ @input="handle"
33
+ />
34
+ <r-icon v-html="$r.icons.search"></r-icon>
35
+ </span>
39
36
  </div>
37
+ <r-card
38
+ v-if="open"
39
+ :class="{
40
+ 'card-tile': $attrs.tile !== undefined && $attrs.tile !== false,
41
+ 'to-top': openToTop,
42
+ }"
43
+ class="card-search z-important"
44
+ >
45
+ <r-progress-line
46
+ v-if="loading"
47
+ class="w-full"
48
+ color="color-two"
49
+ ></r-progress-line>
50
+ <r-list
51
+ v-if="list.length > 0"
52
+ :items="list"
53
+ @update:modelValue="listInput"
54
+ >
55
+ <template v-slot="props">
56
+ <slot :item="props.item">
57
+ <div class="list-title">{{ props.item["name"] }}</div>
58
+ </slot>
59
+ </template>
60
+ </r-list>
61
+ <div v-else-if="!loading" class="py-5">
62
+ {{ notFoundMsg }}
63
+ </div>
64
+ </r-card>
40
65
  <transition name="fade" v-if="!noOverlay">
41
66
  <div v-if="open" class="search-shadow"></div>
42
67
  </transition>
43
68
  </template>
44
- <r-btn v-else icon class="mt-5" @click="show=!show">
69
+ <r-btn v-else class="mt-5" icon @click="show = !show">
45
70
  <r-icon v-html="$r.icons.search"></r-icon>
46
71
  </r-btn>
47
72
  </div>
48
73
  </template>
49
74
  <script>
50
-
51
75
  export default {
52
- name: 'r-search-box',
53
- inheritAttrs: false,
76
+ name: "r-search-box",
54
77
  props: {
55
78
  closable: Boolean,
56
79
  notFoundMsg: {
57
80
  type: String,
58
- default: "Can't Find Anything :("
81
+ default: "Can't Find Anything :(",
59
82
  },
83
+ label: String,
60
84
  url: String,
85
+ inputClass: String,
61
86
  query: {
62
87
  type: String,
63
- default: 'search'
88
+ default: "search",
64
89
  },
65
- inputControlClass: [String, Object, Array],
66
90
  modelValue: [String, Number],
67
- autofocus: Boolean,
68
91
  noOverlay: Boolean,
69
92
  openToTop: Boolean,
70
- headers: Object
93
+ categories: Array,
94
+ headers: Object,
71
95
  },
72
- emits:['update:modelValue','select'],
96
+ emits: ["update:modelValue", "select"],
73
97
  data() {
74
98
  return {
75
99
  show: false,
@@ -78,57 +102,63 @@ emits:['update:modelValue','select'],
78
102
  active: false,
79
103
  open: false,
80
104
  idSet: null,
81
- list: []
82
- }
105
+ category: null,
106
+ list: [],
107
+ };
83
108
  },
84
109
  watch: {
85
110
  modelValue() {
86
- this.lazyValue = this.modelValue
87
- }
111
+ this.lazyValue = this.modelValue;
112
+ },
88
113
  },
89
114
  methods: {
90
115
  handleclose() {
91
- this.open = false
116
+ this.open = false;
92
117
  },
93
118
  get() {
94
119
  if (this.url) {
95
- this.loading = true
96
- this.$axios.get(this.url, {
97
- params: {
98
- [this.query]: this.lazyValue
99
- },
100
- headers: this.headers
101
- }).then(({data}) => {
102
- this.list = data
103
- this.loading = false
104
- this.open = true
105
- }, (e) => {
106
- this.loading = false
107
- })
120
+ this.loading = true;
121
+ this.$axios
122
+ .get(this.url, {
123
+ params: {
124
+ [this.query]: this.lazyValue,
125
+ category: this.category,
126
+ },
127
+ headers: this.headers,
128
+ })
129
+ .then(
130
+ ({data}) => {
131
+ this.list = data;
132
+ this.loading = false;
133
+ this.open = true;
134
+ },
135
+ () => {
136
+ this.loading = false;
137
+ }
138
+ );
108
139
  }
109
140
  },
110
141
  handle(e) {
111
- this.lazyValue = e.target.value
112
- this.open = true
113
- this.loading = true
114
- clearTimeout(this.idSet)
142
+ this.lazyValue = e.target.value;
143
+ this.open = true;
144
+ this.loading = true;
145
+ clearTimeout(this.idSet);
115
146
  this.idSet = setTimeout(() => {
116
- this.$emit('update:modelValue', this.lazyValue)
117
- this.get()
118
- }, 1000)
147
+ this.$emit("update:modelValue", this.lazyValue);
148
+ this.get();
149
+ }, 1000);
119
150
  },
120
151
  listInput(e) {
121
- this.$emit('select', e)
122
- this.open = false
123
- }
124
- }
125
- }
126
-
152
+ this.$emit("select", e);
153
+ this.open = false;
154
+ },
155
+ },
156
+ };
127
157
  </script>
128
158
  <style lang="scss">
129
- @import '../../style/include';
159
+ @import "~renusify/style/include";
130
160
 
131
- .#{$prefix}search-container {
161
+ .#{$prefix}search-box {
132
162
  position: relative;
133
163
 
134
164
  .to-top {
@@ -136,35 +166,75 @@ emits:['update:modelValue','select'],
136
166
  }
137
167
 
138
168
  .search-input {
169
+ display: flex;
170
+ align-items: center;
171
+ border: solid 1px var(--color-border);
172
+ color: var(--color-text-primary);
173
+ position: relative;
174
+ border-radius: map-get($borders, "md");
175
+
139
176
  .sheet {
140
177
  transition: 0.5s all ease;
141
178
  }
179
+ }
142
180
 
143
- .card-select {
144
- position: absolute;
145
- left: 0;
146
- width: 100%;
147
- overflow-y: auto;
148
- max-height: 300px;
181
+ .search-open {
182
+ border-bottom-left-radius: 0px !important;
183
+ border-bottom-right-radius: 0px !important;
184
+ }
149
185
 
150
- &:not(.card-tile) {
151
- border-radius: 0 0 20px 20px;
152
- }
153
- }
186
+ input {
187
+ outline: none;
188
+ line-height: 20px;
189
+ padding: 12px;
190
+ max-width: 100%;
191
+ min-width: 0px;
192
+ caret-color: var(--color-text-primary);
193
+ }
154
194
 
155
- .search-active-border:not(.input-tile) {
156
- border-radius: 20px 20px 0 0;
157
- }
195
+ .card-search {
196
+ position: absolute;
197
+ left: 0;
198
+ width: 100%;
199
+ overflow-y: auto;
200
+ max-height: 300px;
201
+ border-radius: 0 0 map-get($borders, "md") map-get($borders, "md");
158
202
  }
159
203
 
160
204
  .search-shadow {
161
205
  position: fixed;
162
206
  width: 100vw;
163
207
  height: 100vh;
164
- z-index: map_get($z-index, 'medium');
208
+ z-index: map_get($z-index, "medium");
165
209
  top: 0;
166
210
  left: 0;
167
211
  backdrop-filter: blur(3px) grayscale(30%);
168
212
  }
213
+
214
+ .#{$prefix}select-container {
215
+ .input-control {
216
+ border-radius: 0;
217
+ min-height: 30px;
218
+ @include ltr() {
219
+ border-right: 1px solid var(--color-border) !important;
220
+ }
221
+
222
+ @include rtl() {
223
+ border-left: 1px solid var(--color-border) !important;
224
+ }
225
+ }
226
+
227
+ .card-select {
228
+ top: -6px;
229
+ border-radius: map-get($borders, "md");
230
+ @include ltr() {
231
+ border-top-right-radius: 0 !important;
232
+ }
233
+
234
+ @include rtl() {
235
+ border-top-left-radius: 0 !important;
236
+ }
237
+ }
238
+ }
169
239
  }
170
240
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "renusify",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Vue3 Framework",
5
5
  "keywords": [
6
6
  "vuejs",
package/style/colors.scss CHANGED
@@ -73,7 +73,7 @@ $colorsList: (
73
73
  @each $color_name, $color_value in $colorMain {
74
74
  .color-#{$color_name} {
75
75
  @include background-color(var(--color-#{$color_name}), true);
76
- @include text-color(var(--color-#{$color_name}-text),true);
76
+ @include text-color(var(--color-#{$color_name}-text));
77
77
  }
78
78
  .color-#{$color_name}-text {
79
79
  @include text-color(var(--color-#{$color_name}), true);
@@ -100,6 +100,7 @@ $utilities: map-merge(
100
100
  center: center,
101
101
  space-between: space-between,
102
102
  space-around: space-around,
103
+ space-evenly: space-evenly,
103
104
  )
104
105
  ),
105
106
  "align-items": (