renusify 2.5.2 → 3.0.0

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 (212) hide show
  1. package/components/app/index.vue +74 -22
  2. package/components/app/toast/index.vue +76 -71
  3. package/components/app/toast/toast.vue +62 -44
  4. package/components/avatar/index.vue +208 -84
  5. package/components/button/buttonConfirm.vue +53 -26
  6. package/components/button/buttonGroup.js +0 -2
  7. package/components/button/buttonGroup.vue +310 -62
  8. package/components/button/index.vue +584 -100
  9. package/components/calendar/index.js +0 -2
  10. package/components/calendar/index.vue +326 -262
  11. package/components/calendar/month.vue +64 -55
  12. package/components/calendar/year.vue +30 -25
  13. package/components/card/index.vue +139 -59
  14. package/components/codeEditor/highlightCss.vue +38 -39
  15. package/components/codeEditor/highlightHtml.vue +64 -64
  16. package/components/codeEditor/highlightJs.vue +37 -38
  17. package/components/codeEditor/index.vue +129 -79
  18. package/components/codeEditor/run.vue +225 -39
  19. package/components/codeEditor/useCodeFormatter.js +150 -0
  20. package/components/confirm/index.vue +139 -80
  21. package/components/container/col.vue +5 -4
  22. package/components/container/divider.vue +28 -19
  23. package/components/container/index.vue +34 -15
  24. package/components/container/row.vue +26 -9
  25. package/components/container/spacer.vue +2 -4
  26. package/components/container/style.scss +3 -0
  27. package/components/content/index.vue +49 -32
  28. package/components/cropper/index.vue +401 -244
  29. package/components/float/index.vue +542 -415
  30. package/components/form/addressInput/index.vue +184 -109
  31. package/components/form/camInput/index.vue +370 -244
  32. package/components/form/checkInput/index.vue +138 -71
  33. package/components/form/checkboxInput/index.vue +87 -47
  34. package/components/form/colorInput/Alpha.vue +81 -83
  35. package/components/form/colorInput/Hue.vue +91 -68
  36. package/components/form/colorInput/Preview.vue +43 -47
  37. package/components/form/colorInput/Saturation.vue +101 -86
  38. package/components/form/colorInput/index.vue +71 -39
  39. package/components/form/colorInput/picker.vue +111 -106
  40. package/components/form/colorInput/useColor.js +153 -0
  41. package/components/form/dateInput/index.vue +691 -356
  42. package/components/form/dateInput/month.vue +63 -54
  43. package/components/form/dateInput/year.vue +35 -25
  44. package/components/form/fileInput/index.js +0 -1
  45. package/components/form/fileInput/index.vue +263 -106
  46. package/components/form/fileInput/single.vue +323 -164
  47. package/components/form/groupInput/index.vue +199 -101
  48. package/components/form/index.vue +189 -83
  49. package/components/form/input/index.vue +416 -377
  50. package/components/form/jsonInput/JsonView.vue +54 -56
  51. package/components/form/jsonInput/index.vue +247 -165
  52. package/components/form/maskInput/index.vue +252 -132
  53. package/components/form/numberInput/index.js +0 -1
  54. package/components/form/numberInput/index.vue +226 -117
  55. package/components/form/passwordInput/index.js +2 -1
  56. package/components/form/passwordInput/index.vue +269 -102
  57. package/components/form/radioInput/index.vue +143 -72
  58. package/components/form/rangeInput/index.vue +280 -167
  59. package/components/form/ratingInput/index.vue +57 -57
  60. package/components/form/selectInput/index.js +1 -3
  61. package/components/form/selectInput/index.vue +584 -296
  62. package/components/form/switchInput/index.vue +73 -59
  63. package/components/form/telInput/index.js +0 -1
  64. package/components/form/telInput/index.vue +238 -135
  65. package/components/form/textArea/index.vue +72 -35
  66. package/components/form/textEditor/index.vue +739 -0
  67. package/components/form/{text-editor → textEditor}/style.scss +8 -16
  68. package/components/form/textInput/index.vue +54 -32
  69. package/components/form/timeInput/index.vue +82 -55
  70. package/components/form/timeInput/range.vue +115 -94
  71. package/components/form/timeInput/timepicker.vue +382 -449
  72. package/components/form/uniqueInput/index.vue +105 -48
  73. package/components/form/unitInput/index.vue +139 -84
  74. package/components/formCreator/index.js +0 -1
  75. package/components/formCreator/index.vue +314 -148
  76. package/components/highlight/index.vue +41 -25
  77. package/components/highlight/style.scss +2 -2
  78. package/components/highlight/{mixin.js → useHighlight.js} +181 -160
  79. package/components/icon/index.vue +79 -33
  80. package/components/img/index.vue +249 -147
  81. package/components/img/preview.vue +180 -198
  82. package/components/img/svgImg.vue +42 -39
  83. package/components/index.js +5 -20
  84. package/components/infinite/index.js +1 -2
  85. package/components/infinite/index.vue +248 -66
  86. package/components/map/index.vue +428 -261
  87. package/components/map/route.vue +794 -487
  88. package/components/map/select.vue +118 -58
  89. package/components/menu/index.vue +201 -91
  90. package/components/meta/meta.js +26 -3
  91. package/components/modal/index.vue +382 -156
  92. package/components/notify/index.vue +204 -86
  93. package/components/notify/notification.vue +38 -55
  94. package/components/progress/circle.vue +189 -70
  95. package/components/progress/line.vue +266 -46
  96. package/components/searchBox/index.js +1 -3
  97. package/components/searchBox/index.vue +194 -101
  98. package/components/skeleton/index.vue +45 -20
  99. package/components/slider/index.vue +318 -156
  100. package/components/swiper/index.vue +254 -106
  101. package/components/table/crud/footer.vue +77 -53
  102. package/components/table/crud/header.vue +71 -72
  103. package/components/table/crud/index.vue +629 -399
  104. package/components/table/index.vue +721 -278
  105. package/components/timeAgo/index.vue +145 -96
  106. package/components/tour/index.vue +338 -235
  107. package/components/tree/index.vue +235 -89
  108. package/components/tree/tree-element.vue +106 -106
  109. package/directive/animate/index.js +77 -0
  110. package/directive/clickOutSide/index.js +98 -0
  111. package/directive/drag/index.js +153 -0
  112. package/directive/index.js +11 -13
  113. package/directive/intersect/index.js +263 -0
  114. package/directive/mask/index.js +67 -0
  115. package/directive/parallax/index.js +78 -0
  116. package/directive/ripple/index.js +14 -0
  117. package/directive/scroll/index.js +244 -0
  118. package/directive/sortable/index.js +274 -0
  119. package/directive/title/index.js +75 -0
  120. package/directive/touch/index.js +268 -0
  121. package/index.js +10 -8
  122. package/package.json +5 -2
  123. package/plugins/validation/Validate.js +88 -79
  124. package/scripts/generate-docs.mjs +226 -0
  125. package/scripts/menu.mjs +240 -0
  126. package/scripts/parser.mjs +1086 -0
  127. package/style/_index.scss +7 -0
  128. package/style/app.scss +13 -65
  129. package/style/colors.scss +5 -22
  130. package/style/functions/index.scss +8 -0
  131. package/style/mixins/index.scss +17 -5
  132. package/style/variables/base.scss +154 -175
  133. package/style/variables/color.scss +0 -12
  134. package/style/variables/utilities.scss +0 -180
  135. package/tools/helper.js +0 -8
  136. package/tools/icons.js +6 -1
  137. package/tools/root.js +71 -0
  138. package/components/app/style.scss +0 -41
  139. package/components/app/toast/style.scss +0 -20
  140. package/components/avatar/style.scss +0 -32
  141. package/components/bar/bottomNav.js +0 -1
  142. package/components/bar/bottomNav.vue +0 -28
  143. package/components/bar/bottomNavigationCircle.js +0 -2
  144. package/components/bar/bottomNavigationCircle.vue +0 -99
  145. package/components/bar/scss/bottomNav.scss +0 -67
  146. package/components/bar/scss/toolbar.scss +0 -174
  147. package/components/bar/toolbar/index.js +0 -8
  148. package/components/bar/toolbar/index.vue +0 -35
  149. package/components/bar/toolbar/laptop.vue +0 -33
  150. package/components/bar/toolbar/menuChilds.vue +0 -41
  151. package/components/bar/toolbar/menuLaptop.vue +0 -41
  152. package/components/bar/toolbar/menuMob.vue +0 -39
  153. package/components/bar/toolbar/mixin.js +0 -43
  154. package/components/bar/toolbar/mobile.vue +0 -34
  155. package/components/breadcrumb/bredcrumbItem.vue +0 -39
  156. package/components/breadcrumb/index.js +0 -3
  157. package/components/breadcrumb/index.vue +0 -71
  158. package/components/breadcrumb/style.scss +0 -51
  159. package/components/button/style.scss +0 -411
  160. package/components/card/style.scss +0 -86
  161. package/components/chart/chart.js +0 -1
  162. package/components/chart/chart.vue +0 -69
  163. package/components/chart/worldMap.js +0 -2
  164. package/components/chart/worldMap.vue +0 -1112
  165. package/components/chat/MessageList.vue +0 -163
  166. package/components/chat/chatInput.vue +0 -150
  167. package/components/chat/chatMsg.vue +0 -276
  168. package/components/chat/index.js +0 -11
  169. package/components/chat/index.vue +0 -113
  170. package/components/chip/index.js +0 -3
  171. package/components/chip/index.vue +0 -77
  172. package/components/chip/style.scss +0 -199
  173. package/components/codeEditor/mixin.js +0 -145
  174. package/components/countdown/index.js +0 -1
  175. package/components/countdown/index.vue +0 -105
  176. package/components/form/colorInput/mixin.js +0 -132
  177. package/components/form/fileInput/file.js +0 -148
  178. package/components/form/telInput/assets/flags.png +0 -0
  179. package/components/form/telInput/assets/flags@2x.png +0 -0
  180. package/components/form/text-editor/index.vue +0 -705
  181. package/components/icon/style.scss +0 -17
  182. package/components/infinite/div.js +0 -6
  183. package/components/infinite/div.vue +0 -193
  184. package/components/infinite/page.js +0 -3
  185. package/components/infinite/page.vue +0 -105
  186. package/components/list/index.js +0 -3
  187. package/components/list/index.vue +0 -122
  188. package/components/list/style.scss +0 -66
  189. package/components/message/index.js +0 -4
  190. package/components/message/index.vue +0 -40
  191. package/components/modal/style.scss +0 -146
  192. package/components/nestable/NestableItem.vue +0 -307
  193. package/components/nestable/editable.js +0 -44
  194. package/components/nestable/index.js +0 -1
  195. package/components/nestable/index.vue +0 -226
  196. package/components/nestable/methods.js +0 -416
  197. package/components/progress/style.scss +0 -229
  198. package/components/table/style.scss +0 -338
  199. package/components/tabs/index.js +0 -3
  200. package/components/tabs/index.vue +0 -151
  201. package/components/timeline/index.js +0 -6
  202. package/components/timeline/index.vue +0 -76
  203. package/directive/resize/index.js +0 -30
  204. package/directive/skeleton/index.js +0 -27
  205. package/directive/skeleton/style.scss +0 -37
  206. package/plugins/request/Request.js +0 -68
  207. package/style/animation.scss +0 -94
  208. package/style/style.scss +0 -8
  209. package/tools/rootable.js +0 -75
  210. /package/components/form/{text-editor → textEditor}/index.js +0 -0
  211. /package/components/form/{text-editor → textEditor}/preview.js +0 -0
  212. /package/components/form/{text-editor → textEditor}/preview.vue +0 -0
@@ -1,35 +1,87 @@
1
1
  <template>
2
- <div :class="{
3
- [$r.prefix+'app']:true,
4
- 'app-rtl': isRtl,
5
- 'app-ltr': !isRtl
6
- }" :id="id">
2
+ <div :id="id" :class="classes">
7
3
  <div class="app-wrap" :style="{'min-height':$r.breakpoint.height+'px'}">
4
+ <!--
5
+ Main content slot for the application wrapper.
6
+ @example
7
+ <div class="color-one pa-12">
8
+ <r-btn>test</r-btn>
9
+ </div>
10
+ -->
8
11
  <slot></slot>
9
12
  </div>
10
13
  <r-toast></r-toast>
11
14
  </div>
12
15
  </template>
13
16
 
14
- <script>
15
- import './style.scss'
16
-
17
- export default {
18
- name: 'r-app',
19
- props: {
20
- id: {
21
- type: [String, Number],
22
- default: 'renusify'
23
- },
24
- rtl: {type: Boolean, default: undefined}
17
+ <script setup>
18
+ import {computed, inject} from 'vue'
19
+
20
+ const renusify = inject('renusify')
21
+
22
+ const props = defineProps({
23
+ /**
24
+ * The ID attribute for the root element
25
+ */
26
+ id: {
27
+ type: [String, Number],
28
+ default: 'renusify'
25
29
  },
26
- computed: {
27
- isRtl() {
28
- if (this.rtl !== undefined) {
29
- return this.rtl
30
+ /**
31
+ * Controls the text direction (Right-to-Left).
32
+ * If undefined, uses the global RTL setting from Renusify.
33
+ */
34
+ rtl: {type: Boolean, default: undefined}
35
+ })
36
+
37
+ const classes = computed(() => ({
38
+ [renusify.$r.prefix + 'app']: true,
39
+ 'app-rtl': props.rtl ?? renusify.$r.rtl,
40
+ 'app-ltr': !(props.rtl ?? renusify.$r.rtl)
41
+ }))
42
+ </script>
43
+
44
+ <style lang="scss">
45
+ @use "../../style" as *;
46
+ @use "../../style/base";
47
+ @use "../../style/app";
48
+ @use '../../style/colors';
49
+ @use '../../style/transitions';
50
+
51
+ .#{$prefix}app {
52
+ display: flex;
53
+
54
+ background: var(--color-sheet);
55
+ color: var(--color-on-sheet);
56
+
57
+
58
+ &.app-rtl {
59
+ direction: rtl;
60
+ text-align: right;
61
+ }
62
+
63
+ &.app-ltr {
64
+ direction: ltr;
65
+ text-align: left;
66
+ }
67
+
68
+ .app-wrap {
69
+ max-width: 100%;
70
+ width: 100%;
71
+ }
72
+
73
+ }
74
+
75
+ // Firefox overrides
76
+ @include firefox() {
77
+ @media print {
78
+ .#{$prefix}app {
79
+ display: block;
80
+
81
+ &-wrap {
82
+ display: block
30
83
  }
31
- return this.$r.rtl
32
84
  }
33
85
  }
34
86
  }
35
- </script>
87
+ </style>
@@ -1,77 +1,82 @@
1
1
  <template>
2
- <toast :time="time"
3
- :type="type"
4
- :closable="closable"
5
- :action="action"
6
- :actionName="actionName"
7
- v-model="show">
8
- {{ msg }}
9
- </toast>
2
+ <toast v-model="show"
3
+ :action="action"
4
+ :actionName="actionName"
5
+ :closable="closable"
6
+ :time="time"
7
+ :type="type">
8
+ {{ msg }}
9
+ </toast>
10
10
  </template>
11
11
 
12
- <script>
12
+ <script setup>
13
+ import {ref, onMounted, onBeforeUnmount, inject} from 'vue'
13
14
  import Toast from './toast.vue'
14
15
 
15
16
  const EVENTS = ['online', 'offline', 'load']
16
- export default {
17
- name: 'r-toast',
18
- props:{
19
- showOnlineStatus: {type:Boolean,default:true}
20
- },
21
- components: {
22
- Toast
23
- },
24
- data() {
25
- return {
26
- msg: '',
27
- time: 3000,
28
- closable: true,
29
- show: false,
30
- type: 'info',
31
- setTimeout_id:null,
32
- action:undefined,
33
- actionName:undefined
34
- }
35
- },
36
- mounted() {
37
- window.renusifyBus.on(
38
- 'toast', (data) => {
39
- if (this.show) {
40
- this.show = false
41
- clearTimeout(this.setTimeout_id)
42
- this.setTimeout_id=setTimeout(() => {
43
- this.build(data)
44
- }, 300)
45
- } else {
46
- this.build(data)
47
- }
48
- }
49
- )
50
- if(this.showOnlineStatus) {
51
- EVENTS.forEach(event => window.addEventListener(event, this.updateOnlineStatus))
52
- }
53
- },
54
- beforeUnmount() {
55
- EVENTS.forEach(event => window.removeEventListener(event, this.updateOnlineStatus))
56
- },
57
- methods: {
58
- build(data) {
59
- const option = data.options || {}
60
- this.msg = data.msg
61
- this.action=option.action
62
- this.actionName=option.action_name
63
- this.closable=this.$helper.ifHas(option, true, 'closable')
64
- this.time = this.$helper.ifHas(option, 3000, 'time')
65
- this.type = this.$helper.ifHas(option, 'info', 'type')
66
- this.show = true
67
- },
68
- updateOnlineStatus() {
69
- if (!navigator.onLine) {
70
- this.$toast(this.$t('no_internet','renusify'), {type: 'warning', time: -1})
71
- } else {
72
- this.$toast(this.$t('no_internet','renusify'), {type: 'warning', time: 0})
73
- }
74
- }
75
- }
76
- }
77
- </script>
17
+
18
+ const props = defineProps({
19
+ /**
20
+ * Whether to show online/offline status notifications
21
+ */
22
+ showOnlineStatus: {type: Boolean, default: true}
23
+ })
24
+
25
+ const msg = ref('')
26
+ const time = ref(3000)
27
+ const closable = ref(true)
28
+ const show = ref(false)
29
+ const type = ref('info')
30
+ const setTimeout_id = ref(null)
31
+ const action = ref(undefined)
32
+ const actionName = ref(undefined)
33
+
34
+ const renusify = inject('renusify')
35
+ const $helper = renusify.$helper
36
+ const $toast = renusify.$toast
37
+ const $t = renusify.$t
38
+
39
+ // Methods
40
+ const build = (data) => {
41
+ const option = data.options || {}
42
+ msg.value = data.msg
43
+ action.value = option.action
44
+ actionName.value = option.action_name
45
+ closable.value = $helper.ifHas(option, true, 'closable')
46
+ time.value = $helper.ifHas(option, 3000, 'time')
47
+ type.value = $helper.ifHas(option, 'info', 'type')
48
+ show.value = true
49
+ }
50
+
51
+ const updateOnlineStatus = () => {
52
+ if (!navigator.onLine) {
53
+ $toast($t('no_internet', 'renusify'), {type: 'warning', time: -1})
54
+ } else {
55
+ $toast($t('no_internet', 'renusify'), {type: 'warning', time: 0})
56
+ }
57
+ }
58
+
59
+ const handleToastEvent = (data) => {
60
+ if (show.value) {
61
+ show.value = false
62
+ clearTimeout(setTimeout_id.value)
63
+ setTimeout_id.value = setTimeout(() => {
64
+ build(data)
65
+ }, 300)
66
+ } else {
67
+ build(data)
68
+ }
69
+ }
70
+
71
+ onMounted(() => {
72
+ window.renusifyBus.on('toast', handleToastEvent)
73
+
74
+ if (props.showOnlineStatus) {
75
+ EVENTS.forEach(event => window.addEventListener(event, updateOnlineStatus))
76
+ }
77
+ })
78
+
79
+ onBeforeUnmount(() => {
80
+ EVENTS.forEach(event => window.removeEventListener(event, updateOnlineStatus))
81
+ })
82
+ </script>
@@ -23,53 +23,71 @@
23
23
 
24
24
  </template>
25
25
 
26
- <script>
27
- import './style.scss'
26
+ <script setup>
27
+ import {ref, watch, inject} from 'vue'
28
28
 
29
- export default {
30
- name: 'r-toast',
31
- props: {
32
- type: {
33
- type: String,
34
- default: 'info',
35
- validator: function (value) {
36
- return ['info', 'warning', 'error', 'success'].indexOf(value) !== -1
37
- }
38
- },
39
- modelValue: Boolean,
40
- closable: Boolean,
41
- action: Function,
42
- actionName: String,
43
- time: {
44
- type: Number,
45
- default: 3000
29
+ const props = defineProps({
30
+ type: {
31
+ type: String,
32
+ default: 'info',
33
+ validator: function (value) {
34
+ return ['info', 'warning', 'error', 'success'].indexOf(value) !== -1
46
35
  }
47
36
  },
48
- emits:['update:modelValue'],
49
- data(){
50
- return{
51
- setTimeout_id:null
52
- }
53
- },
54
- methods: {
55
- delay () {
56
- if (this.time !== -1) {
57
- clearTimeout(this.setTimeout_id)
58
- this.setTimeout_id=setTimeout(() => {
59
- this.close()
60
- }, this.time)
61
- }
62
- },
63
- close () {
64
- this.$emit('update:modelValue', false)
65
- }
66
- },
67
- watch: {
68
- modelValue (nVal) {
69
- if (nVal === true) {
70
- this.delay()
71
- }
72
- }
37
+ modelValue: Boolean,
38
+ closable: Boolean,
39
+ action: Function,
40
+ actionName: String,
41
+ time: {
42
+ type: Number,
43
+ default: 3000
44
+ }
45
+ })
46
+
47
+ const emit = defineEmits(['update:modelValue'])
48
+
49
+ const setTimeout_id = ref(null)
50
+ const $r = inject('renusify').$r
51
+
52
+ // Methods
53
+ const delay = () => {
54
+ if (props.time !== -1) {
55
+ clearTimeout(setTimeout_id.value)
56
+ setTimeout_id.value = setTimeout(() => {
57
+ close()
58
+ }, props.time)
73
59
  }
74
60
  }
61
+
62
+ const close = () => {
63
+ emit('update:modelValue', false)
64
+ }
65
+
66
+ // Watchers
67
+ watch(() => props.modelValue, (nVal) => {
68
+ if (nVal === true) {
69
+ delay()
70
+ }
71
+ })
75
72
  </script>
73
+
74
+ <style lang="scss">
75
+ @use "sass:map";
76
+ @use "../../../style" as *;
77
+
78
+ .#{$prefix}toast {
79
+ max-width: 500px;
80
+ width: 95vw;
81
+ position: fixed;
82
+ bottom: 15px;
83
+ left: 50%;
84
+ transform: translateX(-50%);
85
+ z-index: map.get($z-index, 'veryImportant');
86
+
87
+ .toast-content {
88
+ padding: 15px;
89
+ display: flex;
90
+ align-items: center;
91
+ }
92
+ }
93
+ </style>
@@ -1,90 +1,214 @@
1
1
  <template>
2
- <div :class="`${$r.prefix}avatar ${tile?'avatar-tile':''}`"
3
- :style="`height:${size}px;width:${size}px;`">
4
- <div class="avatar-content" :class="`${!flat?'elevation-'+elevation:''}`" :key="k">
5
- <slot></slot>
6
- </div>
7
- <r-btn :loading="loading" v-if="editable" @click.prevent.stop="pickFile" icon class="avatar-edit color-info">
8
- <r-icon v-html="$r.icons.camera"></r-icon>
9
- </r-btn>
10
- <input v-if="editable" accept="image/*"
11
- @change="addFile()"
12
- ref="file"
13
- class="d-none"
14
- type="file"
15
- >
2
+ <div
3
+ :class="avatarClasses"
4
+ :style="avatarStyle"
5
+ >
6
+ <div :key="k" :class="contentClasses">
7
+ <!-- Default slot for avatar content.
8
+ @example <r-img src="/storage/img/avatar.jpg" width="80" height="80"></r-img>-->
9
+ <slot></slot>
16
10
  </div>
11
+
12
+ <r-btn
13
+ v-if="editable"
14
+ :loading="loading"
15
+ class="avatar-edit-button color-info"
16
+ icon
17
+ @click.prevent.stop="pickFile"
18
+ >
19
+ <r-icon v-html="$r.icons.edit"></r-icon>
20
+ </r-btn>
21
+
22
+ <input
23
+ v-if="editable"
24
+ ref="file"
25
+ accept="image/*"
26
+ class="d-none"
27
+ type="file"
28
+ @change="addFile()"
29
+ >
30
+ </div>
17
31
  </template>
18
32
 
19
- <script>
20
- import './style.scss'
21
-
22
- export default {
23
- name: 'r-avatar',
24
- props: {
25
- size: {
26
- type: [Number, String],
27
- default: 48
28
- },
29
- elevation: {
30
- type: String,
31
- default: 'none'
32
- },
33
- editable: {
34
- type: String
35
- },
36
- flat: Boolean,
37
- tile: Boolean,
38
- headers: Object
39
- },
40
- data() {
41
- return {
42
- k:0,
43
- files: [],
44
- loading: false
45
- }
46
- },
47
- created(){
48
- if(!this.$r.icons.camera){
49
- this.$r.icons.camera = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path fill="currentColor" d="M3 4V1h2v3h3v2H5v3H3V6H0V4m6 6V7h3V4h7l1.8 2H21c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V10m10 9c4.45 0 6.69-5.38 3.54-8.54C13.39 7.31 8 9.55 8 14c0 2.76 2.24 5 5 5m-3.2-5c0 2.85 3.45 4.28 5.46 2.26c2.02-2.01.59-5.46-2.26-5.46A3.21 3.21 0 0 0 9.8 14Z"/></svg>'
50
- }
51
- },
52
- methods: {
53
- addFile() {
54
- this.CancelTokenSource = this.$axios.CancelToken.source()
55
- this.files = this.$refs.file.files
56
- this.saveImage()
57
- },
58
- pickFile() {
59
- this.$refs.file.click()
60
- },
61
- saveImage() {
62
- this.loading = true
63
- let fileData = new FormData()
64
- fileData.append('file', this.files[0])
65
- let headers = this.headers
66
- if (!headers) {
67
- headers = {}
68
- }
69
- headers['Content-Type'] = 'multipart/form-data'
70
- this.$axios.post(this.editable, fileData,
71
- {
72
- headers: headers,
73
- onUploadProgress: function (progressEvent) {
74
- this.uploadPercentage = Math.min(parseInt(Math.floor((progressEvent.loaded * 100) / progressEvent.total)), 98)
75
- }.bind(this),
76
- cancelToken: this.CancelTokenSource.token
77
- }
78
- )
79
- .then(() => {
80
- this.loading = false
81
- this.k+=1
82
- }, () => {
83
- this.loading = false
84
- })
85
- }
86
-
87
- }
33
+ <script setup>
34
+ import {ref, computed, inject} from 'vue'
88
35
 
89
- }
36
+ const props = defineProps({
37
+ /**
38
+ * Size of the avatar in pixels
39
+ * @type {Number|String}
40
+ * @default 48
41
+ */
42
+ size: {
43
+ type: [Number, String],
44
+ default: 48
45
+ },
46
+ /**
47
+ * Elevation level for the avatar shadow
48
+ * @type {String}
49
+ * @default 'sm'
50
+ */
51
+ elevation: {
52
+ type: String,
53
+ default: 'sm'
54
+ },
55
+ /**
56
+ * API endpoint URL for uploading avatar images when editable
57
+ * @type {String}
58
+ */
59
+ editable: {
60
+ type: String
61
+ },
62
+ /**
63
+ * Removes elevation/shadow from avatar
64
+ * @type {Boolean}
65
+ */
66
+ flat: Boolean,
67
+ /**
68
+ * Removes border radius making corners square
69
+ * @type {Boolean}
70
+ */
71
+ tile: Boolean,
72
+ /**
73
+ * Additional headers for the image upload request
74
+ * @type {Object}
75
+ */
76
+ headers: Object
77
+ })
78
+
79
+ // Reactive data
80
+ const k = ref(0)
81
+ const files = ref([])
82
+ const loading = ref(false)
83
+ const CancelTokenSource = ref(null)
84
+ const file = ref(null)
85
+
86
+ // Inject dependencies
87
+ const $r = inject('renusify').$r
88
+ const $axios = inject('axios')
89
+
90
+ // Computed properties
91
+ const avatarClasses = computed(() => [
92
+ `${$r.prefix}avatar`,
93
+ 'd-inline-flex',
94
+ 'h-center',
95
+ 'v-center'
96
+ ])
97
+
98
+ const contentClasses = computed(() => [
99
+ 'avatar-content',
100
+ 'd-flex',
101
+ 'h-center',
102
+ 'v-center',
103
+ {
104
+ 'br-circle': !props.tile,
105
+ 'br-none': props.tile,
106
+ [`elevation-${props.elevation}`]: !props.flat
107
+ }
108
+ ])
109
+
110
+ const avatarStyle = computed(() => ({
111
+ height: `${props.size}px`,
112
+ width: `${props.size}px`
113
+ }))
114
+
115
+ // Methods
116
+ /**
117
+ * Triggers file picker dialog
118
+ */
119
+ const pickFile = () => {
120
+ file.value.click()
121
+ }
122
+
123
+ /**
124
+ * Handles file selection and initiates upload
125
+ */
126
+ const addFile = () => {
127
+ files.value = file.value.files
128
+ if (files.value.length > 0) {
129
+ saveImage()
130
+ }
131
+ }
132
+
133
+ const uploadPercentage = ref(0)
134
+ /**
135
+ * Uploads selected image to the specified endpoint
136
+ */
137
+ const saveImage = () => {
138
+ loading.value = true
139
+ uploadPercentage.value = 0
140
+ const fileData = new FormData()
141
+ fileData.append('file', files.value[0])
142
+
143
+ const headers = {
144
+ 'Content-Type': 'multipart/form-data',
145
+ ...props.headers
146
+ }
147
+
148
+ CancelTokenSource.value = $axios.CancelToken.source()
149
+
150
+ $axios.post(props.editable, fileData, {
151
+ headers,
152
+ onUploadProgress: (progressEvent) => {
153
+ uploadPercentage.value = Math.min(
154
+ parseInt(Math.floor((progressEvent.loaded * 100) / progressEvent.total)),
155
+ 98
156
+ )
157
+ },
158
+ cancelToken: CancelTokenSource.value.token
159
+ })
160
+ .then(() => {
161
+ loading.value = false
162
+ k.value += 1
163
+ })
164
+ .catch(() => {
165
+ loading.value = false
166
+ })
167
+ }
90
168
  </script>
169
+
170
+ <style lang="scss">
171
+ @use "../../style" as *;
172
+
173
+ .#{$prefix}avatar {
174
+ position: relative;
175
+ border-radius: inherit;
176
+
177
+ .avatar-content {
178
+ width: 100%;
179
+ height: 100%;
180
+ overflow: hidden;
181
+ background-color: var(--color-sheet);
182
+
183
+ :deep(img),
184
+ :deep(svg) {
185
+ width: 100%;
186
+ height: 100%;
187
+ object-fit: cover;
188
+ display: block;
189
+ }
190
+ }
191
+
192
+ .avatar-edit-button {
193
+ position: absolute !important;
194
+ bottom: -8px;
195
+ right: -8px;
196
+ border-radius: 50% !important;
197
+ width: 32px !important;
198
+ height: 32px !important;
199
+ opacity: 0;
200
+ transform: scale(0.8);
201
+ transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
202
+ }
203
+
204
+ &:hover .avatar-edit-button {
205
+ opacity: 1;
206
+ transform: scale(1);
207
+ }
208
+
209
+ &:hover .avatar-content {
210
+ transform: scale(1.05);
211
+ box-shadow: var(--box-shadow, rgba(0, 0, 0, 0.1)) 0 0.5rem 1rem;
212
+ }
213
+ }
214
+ </style>