@v1nt1248/3nclient-lib 0.1.0 → 0.1.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 (60) hide show
  1. package/README.md +5 -0
  2. package/dist/components/index.d.ts +6 -3
  3. package/dist/components/ui3n-badge-simple.vue.d.ts +13 -13
  4. package/dist/components/ui3n-badge.vue.d.ts +18 -16
  5. package/dist/components/ui3n-breadcrumb.vue.d.ts +15 -13
  6. package/dist/components/ui3n-breadcrumbs.vue.d.ts +17 -15
  7. package/dist/components/ui3n-button.vue.d.ts +23 -19
  8. package/dist/components/ui3n-checkbox.vue.d.ts +17 -15
  9. package/dist/components/ui3n-chip.vue.d.ts +18 -16
  10. package/dist/components/ui3n-dialog.vue.d.ts +4 -5
  11. package/dist/components/ui3n-drop-files.vue.d.ts +16 -14
  12. package/dist/components/ui3n-emoji.vue.d.ts +11 -11
  13. package/dist/components/ui3n-icon.vue.d.ts +12 -12
  14. package/dist/components/ui3n-input.vue.d.ts +12 -5
  15. package/dist/components/ui3n-list.vue.d.ts +12 -39
  16. package/dist/components/ui3n-menu.vue.d.ts +14 -12
  17. package/dist/components/ui3n-notification.vue.d.ts +14 -15
  18. package/dist/components/ui3n-progress-circular.vue.d.ts +43 -0
  19. package/dist/components/ui3n-progress-linear.vue.d.ts +42 -0
  20. package/dist/components/ui3n-step-line-bar.vue.d.ts +30 -0
  21. package/dist/components/ui3n-switch.vue.d.ts +15 -13
  22. package/dist/components/ui3n-table/composables/useTable.d.ts +26 -0
  23. package/dist/components/ui3n-table/types.d.ts +65 -0
  24. package/dist/components/ui3n-table/ui3n-table.vue.d.ts +22 -0
  25. package/dist/components/ui3n-table-sort-icon.vue.d.ts +2 -2
  26. package/dist/components/ui3n-tabs.vue.d.ts +18 -16
  27. package/dist/components/ui3n-text.vue.d.ts +13 -13
  28. package/dist/components/ui3n-virtual-scroll.vue.d.ts +6 -28
  29. package/dist/directives/index.d.ts +0 -1
  30. package/dist/directives/ui3n-click-outside.d.ts +0 -1
  31. package/dist/directives/ui3n-html.d.ts +0 -1
  32. package/dist/index.d.ts +10 -1
  33. package/dist/plugins/dialogs.d.ts +0 -1
  34. package/dist/plugins/i18n.d.ts +0 -1
  35. package/dist/plugins/icons.d.ts +0 -1
  36. package/dist/plugins/index.d.ts +0 -1
  37. package/dist/plugins/notifications.d.ts +0 -1
  38. package/dist/plugins/store-dialogs.d.ts +0 -1
  39. package/dist/plugins/store-notifications.d.ts +0 -1
  40. package/dist/plugins/store-vue-bus.d.ts +0 -1
  41. package/dist/plugins/vue-bus.d.ts +0 -1
  42. package/dist/style.css +1 -1
  43. package/dist/tools/sqlite-on-3nstorage/index.d.ts +0 -1
  44. package/dist/tools/ui.helpers.d.ts +0 -1
  45. package/dist/ui-3n-lib.js +6588 -6551
  46. package/package.json +31 -29
  47. package/src/components/index.ts +46 -4
  48. package/src/components/ui3n-button.vue +6 -0
  49. package/src/components/ui3n-checkbox.vue +1 -1
  50. package/src/components/ui3n-dialog.vue +6 -1
  51. package/src/components/ui3n-input.vue +61 -11
  52. package/src/components/ui3n-progress-circular.vue +188 -0
  53. package/src/components/ui3n-progress-linear.vue +119 -0
  54. package/src/components/ui3n-step-line-bar.vue +66 -0
  55. package/src/components/ui3n-table/composables/useTable.ts +206 -0
  56. package/src/components/ui3n-table/types.ts +80 -0
  57. package/src/components/ui3n-table/ui3n-table.vue +319 -0
  58. package/src/components/{ui3n-table.vue → ui3n-table-simple.vue} +3 -4
  59. package/src/components/ui3n-table-sort-icon.vue +9 -6
  60. package/dist/components/ui3n-table.vue.d.ts +0 -36
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@v1nt1248/3nclient-lib",
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "v1nt1248",
5
- "version": "0.1.0",
5
+ "version": "0.1.2",
6
6
  "description": "Library for 3NWeb clients",
7
7
  "type": "module",
8
8
  "files": [
@@ -36,54 +36,56 @@
36
36
  "dependencies": {
37
37
  "@iconify-icons/ic": "1.2.13",
38
38
  "@iconify/vue": "4.1.2",
39
- "dayjs": "1.11.11",
39
+ "dayjs": "1.11.12",
40
40
  "lodash": "4.17.21",
41
41
  "mitt": "3.0.1",
42
42
  "sanitize-html": "2.13.0",
43
- "tslib": "2.6.3"
43
+ "tslib": "2.6.3",
44
+ "vuedraggable": "^4.1.0"
44
45
  },
45
46
  "peerDependencies": {
46
- "pinia": "2.1.7",
47
- "vue": "3.4.27"
47
+ "pinia": "2.2.1",
48
+ "vue": "3.4.37"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@types/lodash": "4.17.5",
51
52
  "@types/node": "20.14.2",
52
53
  "@types/sanitize-html": "2.11.0",
53
- "@typescript-eslint/eslint-plugin": "7.13.0",
54
- "@typescript-eslint/parser": "7.13.0",
55
- "@vitejs/plugin-vue": "5.0.5",
56
- "@vue/compiler-core": "3.4.27",
54
+ "@typescript-eslint/eslint-plugin": "8.0.1",
55
+ "@typescript-eslint/parser": "8.0.1",
56
+ "@vitejs/plugin-vue": "5.1.2",
57
+ "@vue/compiler-core": "3.4.37",
57
58
  "@vue/eslint-config-typescript": "13.0.0",
58
- "@vue/runtime-core": "3.4.27",
59
+ "@vue/runtime-core": "3.4.37",
59
60
  "@vuedx/typescript-plugin-vue": "0.7.6",
60
- "eslint": "9.4.0",
61
+ "eslint": "9.9.0",
61
62
  "eslint-config-prettier": "^9.1.0",
62
63
  "eslint-plugin-import": "2.29.1",
63
64
  "eslint-plugin-node": "11.1.0",
64
- "eslint-plugin-prettier": "^5.1.3",
65
- "eslint-plugin-promise": "6.2.0",
66
- "eslint-plugin-vue": "9.26.0",
67
- "jackspeak": "3.4.0",
65
+ "eslint-plugin-prettier": "^5.2.1",
66
+ "eslint-plugin-promise": "7.1.0",
67
+ "eslint-plugin-vue": "9.27.0",
68
+ "jackspeak": "4.0.1",
68
69
  "path": "0.12.7",
69
- "pinia": "2.1.7",
70
+ "pinia": "2.2.1",
70
71
  "postcss-html": "1.7.0",
71
- "prettier": "^3.3.2",
72
- "sass": "1.77.5",
73
- "stylelint": "16.6.1",
74
- "stylelint-config-recommended-scss": "14.0.0",
72
+ "prettier": "^3.3.3",
73
+ "sass": "1.77.8",
74
+ "stylelint": "16.8.1",
75
+ "stylelint-config-recommended-scss": "14.1.0",
75
76
  "stylelint-config-recommended-vue": "1.5.0",
76
- "stylelint-config-standard": "36.0.0",
77
- "stylelint-scss": "6.3.1",
78
- "typescript": "5.4.5",
79
- "vite": "5.2.13",
77
+ "stylelint-config-standard": "36.0.1",
78
+ "stylelint-scss": "6.5.0",
79
+ "typescript": "5.5.4",
80
+ "vite": "5.4.0",
80
81
  "vite-plugin-css-injected-by-js": "3.5.1",
81
- "vite-plugin-dts": "3.9.1",
82
- "vitepress": "1.2.3",
83
- "vue": "3.4.27",
84
- "vue-tsc": "2.0.21"
82
+ "vite-plugin-dts": "4.0.2",
83
+ "vite-plugin-vue-devtools": "^7.3.7",
84
+ "vitepress": "1.3.2",
85
+ "vue": "3.4.37",
86
+ "vue-tsc": "2.0.29"
85
87
  },
86
- "packageManager": "pnpm@9.3.0",
88
+ "packageManager": "pnpm@9.7.0",
87
89
  "pnpm": {
88
90
  "peerDependencyRules": {
89
91
  "ignoreMissing": [
@@ -17,10 +17,29 @@ import type { Ui3nCheckboxProps, Ui3nCheckboxEmits, Ui3nCheckboxSlots } from './
17
17
  import Ui3nNotification from './ui3n-notification.vue';
18
18
  import Ui3nTableSortIcon from './ui3n-table-sort-icon.vue';
19
19
  import type { Ui3nTableSortIconProps } from './ui3n-table-sort-icon.vue';
20
- import Ui3nTable from './ui3n-table.vue';
21
- import type { Ui3nTableProps, Ui3nTableEmits } from './ui3n-table.vue';
20
+ import Ui3nTable from './ui3n-table/ui3n-table.vue';
21
+ import type {
22
+ Ui3nTableSort,
23
+ Ui3nTableConfig,
24
+ Ui3nTableHeadProps,
25
+ Ui3nTableBodyProps,
26
+ Ui3nTableGroupActionsSlot,
27
+ Ui3nTableHeaderCellSlot,
28
+ Ui3nTableBodyRowSlotScope,
29
+ Ui3nTableBodyRowSlot,
30
+ Ui3nTableBodyRowCellSlotScope,
31
+ Ui3nTableBodyRowCellSlot,
32
+ Ui3nTableSlots,
33
+ Ui3nTableProps,
34
+ Ui3nTableEmits,
35
+ } from './ui3n-table/types';
22
36
  import Ui3nDialog from './ui3n-dialog.vue';
23
- import type { Ui3nDialogComponentProps, Ui3nDialogComponentEmits, Ui3nDialogProps, Ui3nDialogEvent } from './ui3n-dialog.vue';
37
+ import type {
38
+ Ui3nDialogComponentProps,
39
+ Ui3nDialogComponentEmits,
40
+ Ui3nDialogProps,
41
+ Ui3nDialogEvent,
42
+ } from './ui3n-dialog.vue';
24
43
  import Ui3nMenu from './ui3n-menu.vue';
25
44
  import type { Ui3nMenuProps, Ui3nMenuEmits, Ui3nMenuSlots } from './ui3n-menu.vue';
26
45
  import Ui3nList from './ui3n-list.vue';
@@ -37,6 +56,12 @@ import Ui3nBreadcrumbs from './ui3n-breadcrumbs.vue';
37
56
  import type { Ui3nBreadcrumbsProps } from './ui3n-breadcrumbs.vue';
38
57
  import Ui3nSwitch from './ui3n-switch.vue';
39
58
  import type { Ui3nSwitchProps, Ui3nSwitchEmits, Ui3nSwitchSlots } from './ui3n-switch.vue';
59
+ import Ui3nStepLineBar from './ui3n-step-line-bar.vue';
60
+ import type { Ui3nStepLineBarProps } from './ui3n-step-line-bar.vue';
61
+ import Ui3nProgressLinear from './ui3n-progress-linear.vue';
62
+ import type { Ui3nProgressLinearProps } from './ui3n-progress-linear.vue';
63
+ import Ui3nProgressCircular from './ui3n-progress-circular.vue';
64
+ import type { Ui3nProgressCircularProps } from './ui3n-progress-circular.vue';
40
65
 
41
66
  export {
42
67
  Ui3nIcon,
@@ -69,6 +94,17 @@ export {
69
94
  Ui3nTableSortIcon,
70
95
  Ui3nTableSortIconProps,
71
96
  Ui3nTable,
97
+ Ui3nTableSort,
98
+ Ui3nTableConfig,
99
+ Ui3nTableHeadProps,
100
+ Ui3nTableBodyProps,
101
+ Ui3nTableGroupActionsSlot,
102
+ Ui3nTableHeaderCellSlot,
103
+ Ui3nTableBodyRowSlotScope,
104
+ Ui3nTableBodyRowSlot,
105
+ Ui3nTableBodyRowCellSlotScope,
106
+ Ui3nTableBodyRowCellSlot,
107
+ Ui3nTableSlots,
72
108
  Ui3nTableProps,
73
109
  Ui3nTableEmits,
74
110
  Ui3nDialog,
@@ -103,4 +139,10 @@ export {
103
139
  Ui3nSwitchProps,
104
140
  Ui3nSwitchEmits,
105
141
  Ui3nSwitchSlots,
106
- }
142
+ Ui3nStepLineBar,
143
+ Ui3nStepLineBarProps,
144
+ Ui3nProgressLinear,
145
+ Ui3nProgressLinearProps,
146
+ Ui3nProgressCircular,
147
+ Ui3nProgressCircularProps,
148
+ };
@@ -5,6 +5,7 @@ import Ui3nIcon from './ui3n-icon.vue';
5
5
  export interface Ui3nButtonProps {
6
6
  type?: 'primary' | 'secondary' | 'tertiary' | 'icon' | 'custom';
7
7
  size?: 'regular' | 'small';
8
+ block?: boolean;
8
9
  textColor?: string;
9
10
  color?: string;
10
11
  elevation?: boolean;
@@ -71,6 +72,7 @@ onMounted(() => {
71
72
  $style.button,
72
73
  $style[size],
73
74
  $style[type],
75
+ block && type !== 'icon' && $style.block,
74
76
  icon && $style[`withIcon-${iconPosition}`],
75
77
  elevation && $style.elevation,
76
78
  ]"
@@ -143,6 +145,10 @@ onMounted(() => {
143
145
  }
144
146
  }
145
147
 
148
+ .block {
149
+ width: 100%;
150
+ }
151
+
146
152
  .primary {
147
153
  background: var(--color-bg-button-primary-default);
148
154
  color: var(--color-text-button-primary-default);
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed, onMounted, watch, onBeforeMount, ref, useSlots } from 'vue';
3
3
 
4
- type Ui3nCheckboxValue = boolean | string | number;
4
+ export type Ui3nCheckboxValue = boolean | string | number;
5
5
 
6
6
  export interface Ui3nCheckboxProps {
7
7
  modelValue: Ui3nCheckboxValue;
@@ -36,10 +36,15 @@ export interface Ui3nDialogComponentProps {
36
36
 
37
37
  export interface Ui3nDialogComponentEmits {
38
38
  (ev: 'open', value?: any): void;
39
+
39
40
  (ev: 'before-close', value?: any): void;
41
+
40
42
  (ev: 'close', value?: any): void;
43
+
41
44
  (ev: 'confirm', value?: any): void;
45
+
42
46
  (ev: 'cancel', value?: any): void;
47
+
43
48
  (ev: 'click-overlay', value?: any): void;
44
49
  }
45
50
 
@@ -52,7 +57,7 @@ const data = ref(null);
52
57
  const isValid = ref(true);
53
58
  const dialogElement = ref<HTMLDivElement | null>(null);
54
59
 
55
- const dialogProps = computed<Ui3nDialogProps>(() => ({
60
+ const dialogProps = computed<Required<Omit<Ui3nDialogProps, 'onClose' | 'onConfirm' | 'onCancel'>>>(() => ({
56
61
  teleport: props.dialogProps?.teleport ?? 'body',
57
62
  title: props.dialogProps?.title ?? '',
58
63
  width: props.dialogProps?.width ?? 380,
@@ -7,16 +7,21 @@ import Ui3nButton from './ui3n-button.vue';
7
7
  export interface Ui3nInputProps {
8
8
  modelValue: string;
9
9
  label?: string;
10
+ type?: 'text' | 'password';
10
11
  placeholder?: string;
11
12
  clearable?: boolean;
12
13
  autofocus?: boolean;
13
- rules?: Array<(v: string) => any>;
14
- disabled?: boolean;
15
14
  icon?: string;
16
15
  iconColor?: string;
16
+ rules?: Array<(v: string) => any>;
17
+ displayStateMode?: 'error' | 'success';
18
+ displayStateWithIcon?: boolean;
19
+ displayStateMessage?: string;
20
+ disabled?: boolean;
17
21
  }
18
22
 
19
23
  export interface Ui3nInputEmits {
24
+ (ev: 'init', value: HTMLInputElement): void;
20
25
  (ev: 'input', value: string): void;
21
26
  (ev: 'focus', value: Event): void;
22
27
  (ev: 'blur', value: Event): void;
@@ -39,7 +44,7 @@ const cssIconColor = computed(() => {
39
44
  return 'var(--color-icon-control-primary-disabled)';
40
45
  }
41
46
 
42
- return props.iconColor || 'var(--color-icon-control-primary-default)'
47
+ return props.iconColor || 'var(--color-icon-control-primary-default)';
43
48
  });
44
49
  const isValid = computed(() => {
45
50
  if (!text.value) {
@@ -53,6 +58,8 @@ onMounted(() => {
53
58
  if (props.autofocus && inputElement.value) {
54
59
  inputElement.value.focus();
55
60
  }
61
+
62
+ emits('init', inputElement.value!);
56
63
  });
57
64
 
58
65
  watch(
@@ -124,7 +131,8 @@ const validate = (text: string) => {
124
131
  icon && $style.withIcon,
125
132
  clearable && text && $style.clearable,
126
133
  disabled && $style.disabled,
127
- !!errorMessage && $style.error,
134
+ (!!errorMessage || (displayStateMode === 'error')) && $style.error,
135
+ displayStateMode === 'success' && $style.success,
128
136
  ]"
129
137
  >
130
138
  <label v-if="label" :class="$style.label">
@@ -133,6 +141,8 @@ const validate = (text: string) => {
133
141
 
134
142
  <input
135
143
  ref="inputElement"
144
+ autocomplete="off"
145
+ :type="type || 'text'"
136
146
  :value="text"
137
147
  :placeholder="placeholder"
138
148
  :disabled="disabled"
@@ -152,7 +162,7 @@ const validate = (text: string) => {
152
162
  />
153
163
 
154
164
  <ui3n-button
155
- v-if="clearable && !!text"
165
+ v-if="clearable && !!text && !(displayStateMode === 'success' && displayStateWithIcon && !isFocused)"
156
166
  type="icon"
157
167
  size="small"
158
168
  color="transparent"
@@ -163,8 +173,24 @@ const validate = (text: string) => {
163
173
  @click="clearValue"
164
174
  />
165
175
 
166
- <div v-if="errorMessage" :class="$style.errorText">
167
- {{ errorMessage }}
176
+ <ui3n-icon
177
+ v-if="displayStateMode === 'success' && displayStateWithIcon && !isFocused"
178
+ icon="check-circle-outline"
179
+ :width="16"
180
+ :height="16"
181
+ color="var(--success-content-default)"
182
+ :class="$style.successIcon"
183
+ />
184
+
185
+ <div
186
+ v-if="errorMessage || displayStateMessage"
187
+ :class="[
188
+ $style.fieldMessage,
189
+ errorMessage || (displayStateMode === 'error' && !!displayStateMessage) && $style.errorMessage,
190
+ displayStateMode === 'success' && displayStateMessage && $style.successMessage,
191
+ ]"
192
+ >
193
+ {{ errorMessage || displayStateMessage }}
168
194
  </div>
169
195
  </div>
170
196
  </template>
@@ -208,7 +234,7 @@ const validate = (text: string) => {
208
234
  width: 100%;
209
235
  font-size: var(--font-12);
210
236
  line-height: var(--font-16);
211
- font-weight: 600;
237
+ font-weight: 500;
212
238
  color: var(--color-text-control-primary-default);
213
239
  margin-bottom: var(--spacing-xs);
214
240
  }
@@ -262,12 +288,20 @@ const validate = (text: string) => {
262
288
  z-index: 1;
263
289
  }
264
290
 
265
- .errorText {
291
+ .fieldMessage {
266
292
  font-style: italic;
267
293
  font-size: var(--font-10);
268
294
  font-weight: 400;
269
295
  line-height: 1.4;
270
- color: var(--content-error-default);
296
+ margin-top: 2px;
297
+
298
+ &.errorMessage {
299
+ color: var(--error-content-default);
300
+ }
301
+
302
+ &.successMessage {
303
+ color: var(--success-content-default)
304
+ }
271
305
  }
272
306
 
273
307
  .clearable {
@@ -279,11 +313,27 @@ const validate = (text: string) => {
279
313
  }
280
314
 
281
315
  .error {
316
+ .label {
317
+ color: var(--error-content-default) !important;
318
+ }
319
+
282
320
  .field {
283
- outline: 1px solid var(--content-error-default);
321
+ outline: 1px solid var(--error-content-default) !important;
284
322
  }
285
323
  }
286
324
 
325
+ .success {
326
+ .label {
327
+ color: var(--success-content-default) !important;
328
+ }
329
+ }
330
+
331
+ .successIcon {
332
+ position: absolute;
333
+ right: var(--spacing-s);
334
+ bottom: var(--spacing-s);
335
+ }
336
+
287
337
  .disabled {
288
338
  pointer-events: none;
289
339
  user-select: none;
@@ -0,0 +1,188 @@
1
+ <script lang="ts" setup>
2
+ import { computed, onBeforeMount, onBeforeUnmount, ref, useCssModule } from 'vue';
3
+ import { toNumber } from 'lodash';
4
+
5
+ export interface Ui3nProgressCircularProps {
6
+ value?: number | string;
7
+ size?: number | string;
8
+ width?: number | string;
9
+ withText?: boolean;
10
+ bgColor?: string;
11
+ color?: string;
12
+ indeterminate?: boolean;
13
+ }
14
+
15
+ const thresholdSize = 64;
16
+ const indeterminateModeValues = [1, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80, 85, 90, 95, 99];
17
+
18
+ const props = withDefaults(
19
+ defineProps<Ui3nProgressCircularProps>(),
20
+ {
21
+ value: 0,
22
+ size: 64,
23
+ bgColor: 'var(--color-bg-control-primary-default)',
24
+ color: 'var(--color-bg-control-accent-default)',
25
+ },
26
+ );
27
+
28
+ const $style = useCssModule();
29
+
30
+ const element = ref<HTMLDivElement>();
31
+ const isValueShown = computed(() => props.withText && !props.indeterminate);
32
+
33
+ const innerSize = computed(() => toNumber(props.size));
34
+ const cssSize = computed(() => `${innerSize.value}px`);
35
+ const innerWidth = computed(() => {
36
+ if (props.width) {
37
+ return toNumber(props.width);
38
+ }
39
+
40
+ const diff = innerSize.value - thresholdSize;
41
+ if (diff <= 0) {
42
+ return Math.round(innerSize.value / 10);
43
+ }
44
+
45
+ return 6 + Math.round(diff / 20);
46
+ });
47
+ const cssWidth = computed(() => `${innerWidth.value}px`);
48
+ const innerValue = computed(() => toNumber(props.value));
49
+ const fontSize = computed(() => `${Math.floor((innerSize.value - 2 * innerWidth.value) / 3.2)}px`);
50
+
51
+ const circleRadius = computed(() => Math.round(innerSize.value / 2) - Math.round(innerWidth.value / 2));
52
+ const circumference = computed(() => Math.round(Math.PI * 2 * circleRadius.value));
53
+
54
+ const timerId = ref();
55
+ const indeterminateModeValue = ref(0);
56
+
57
+ const strokeDasharray = computed(() => {
58
+ const value = props.indeterminate ? indeterminateModeValue.value : innerValue.value;
59
+ const dash = Math.round(circumference.value * value! / 100);
60
+ const gap = circumference.value - dash;
61
+ return `${dash} ${gap}`;
62
+ });
63
+
64
+ function changeValueForIndeterminateMode() {
65
+ const indeterminateModeValuesLength = indeterminateModeValues.length;
66
+ let index = 0;
67
+ timerId.value = setInterval(() => {
68
+ indeterminateModeValue.value = indeterminateModeValues[index % indeterminateModeValuesLength];
69
+ index += 1;
70
+ if ((index % indeterminateModeValuesLength) === 1) {
71
+ if (element.value?.classList.contains($style.inverse)) {
72
+ element.value?.classList.remove($style.inverse);
73
+ } else {
74
+ element.value?.classList.add($style.inverse);
75
+ }
76
+ }
77
+ }, 200);
78
+ }
79
+
80
+ onBeforeMount(() => {
81
+ if (props.indeterminate) {
82
+ changeValueForIndeterminateMode();
83
+ }
84
+ });
85
+
86
+ onBeforeUnmount(() => {
87
+ clearInterval(timerId.value);
88
+ });
89
+ </script>
90
+
91
+ <template>
92
+ <div ref="element" :class="[$style.progress, indeterminate && $style.indeterminate]">
93
+ <svg :height="innerSize" :width="innerSize" :class="$style.pie" xmlns="http://www.w3.org/2000/svg">
94
+ <circle :class="$style.background" :r="circleRadius" cx="50%" cy="50%" />
95
+ <circle :class="$style.chart" :r="circleRadius" cx="50%" cy="50%" :stroke-dasharray="strokeDasharray" />
96
+ </svg>
97
+
98
+ <div
99
+ v-if="isValueShown"
100
+ :class="$style.text"
101
+ >
102
+ {{ innerValue }}%
103
+ </div>
104
+ </div>
105
+ </template>
106
+
107
+ <style lang="scss" module>
108
+ .progress {
109
+ --ui3n-progress-circular-size: v-bind(cssSize);
110
+ --ui3n-progress-circular-width: v-bind(cssWidth);
111
+ --ui3n-progress-circular-font-size: v-bind(fontSize);
112
+ --ui3n-progress-circular-bg: v-bind(bgColor);
113
+ --ui3n-progress-circular-color: v-bind(color);
114
+
115
+ position: relative;
116
+ min-width: var(--ui3n-progress-circular-size);
117
+ width: var(--ui3n-progress-circular-size);
118
+ min-height: var(--ui3n-progress-circular-size);
119
+ height: var(--ui3n-progress-circular-size);
120
+ border-radius: 50%;
121
+
122
+ &.inverse {
123
+ .background {
124
+ stroke: var(--ui3n-progress-circular-color) !important;
125
+ }
126
+
127
+ .chart {
128
+ stroke: var(--ui3n-progress-circular-bg) !important;
129
+ }
130
+ }
131
+
132
+ &:not(.indeterminate) {
133
+ .chart {
134
+ transition: 0.2s ease-in-out all;
135
+ }
136
+ }
137
+ }
138
+
139
+ .pie {
140
+ min-width: var(--ui3n-progress-circular-size);
141
+ width: var(--ui3n-progress-circular-size);
142
+ min-height: var(--ui3n-progress-circular-size);
143
+ height: var(--ui3n-progress-circular-size);
144
+ border-radius: 50%;
145
+ transform: rotate(-90deg);
146
+ }
147
+
148
+ .background {
149
+ fill: none;
150
+ stroke: var(--ui3n-progress-circular-bg);
151
+ stroke-width: var(--ui3n-progress-circular-width);
152
+ }
153
+
154
+ .chart {
155
+ fill: none;
156
+ stroke: var(--ui3n-progress-circular-color);
157
+ stroke-width: var(--ui3n-progress-circular-width);
158
+ }
159
+
160
+ .text {
161
+ position: absolute;
162
+ top: 0;
163
+ left: 0;
164
+ width: 100%;
165
+ height: 100%;
166
+ display: flex;
167
+ justify-content: center;
168
+ align-items: center;
169
+ font-size: var(--ui3n-progress-circular-font-size);
170
+ font-weight: 500;
171
+ color: var(--ui3n-progress-circular-color);
172
+ }
173
+
174
+ .indeterminate {
175
+ .pie {
176
+ animation: 0.75s linear 0s infinite normal spin;
177
+ }
178
+ }
179
+
180
+ @keyframes spin {
181
+ 0% {
182
+ transform: rotate(-90deg);
183
+ }
184
+ 100% {
185
+ transform: rotate(270deg);
186
+ }
187
+ }
188
+ </style>
@@ -0,0 +1,119 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+ import { toNumber } from 'lodash';
4
+
5
+ export interface Ui3nProgressLinearProps {
6
+ value?: number | string;
7
+ height?: number | string;
8
+ withText?: boolean;
9
+ bgColor?: string;
10
+ color?: string;
11
+ indeterminate?: boolean;
12
+ }
13
+
14
+ const thresholdHeight = 12;
15
+ const thresholdValue = 5;
16
+
17
+ const props = withDefaults(
18
+ defineProps<Ui3nProgressLinearProps>(),
19
+ {
20
+ value: 0,
21
+ height: 2,
22
+ bgColor: 'var(--color-bg-control-primary-default)',
23
+ color: 'var(--color-bg-control-accent-default)',
24
+ },
25
+ );
26
+
27
+ const isValueShown = computed(() => props.withText && !props.indeterminate);
28
+ const innerHeight = computed(() => toNumber(props.height));
29
+ const cssHeight = computed(() => `${innerHeight.value}px`);
30
+ const innerValue = computed(() => toNumber(props.value));
31
+ const displayValue = computed(() => props.indeterminate
32
+ ? `20%`
33
+ : `${Math.min(innerValue.value, 100)}%`,
34
+ );
35
+ </script>
36
+
37
+ <template>
38
+ <div :class="[$style.progress, indeterminate && $style.indeterminate]">
39
+ <div
40
+ v-if="isValueShown && innerHeight < thresholdHeight"
41
+ :class="[$style.text, $style.above]"
42
+ >
43
+ {{ displayValue }}
44
+ </div>
45
+
46
+ <div :class="$style.body">
47
+ <div :class="$style.value" :style="{ width: displayValue }">
48
+ <div
49
+ v-if="isValueShown && innerHeight >= thresholdHeight && innerValue >= thresholdValue"
50
+ :class="$style.text"
51
+ >
52
+ {{ displayValue }}
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <style lang="scss" module>
60
+ .progress {
61
+ --ui3n-progress-linear-height: v-bind(cssHeight);
62
+ --ui3n-progress-linear-bg: v-bind(bgColor);
63
+ --ui3n-progress-linear-color: v-bind(color);
64
+
65
+ position: relative;
66
+ width: 100%;
67
+ }
68
+
69
+ .body {
70
+ position: relative;
71
+ width: 100%;
72
+ height: var(--ui3n-progress-linear-height);
73
+ border-radius: calc(var(--ui3n-progress-linear-height) - 2px);
74
+ background-color: var(--ui3n-progress-linear-bg);
75
+ overflow-x: hidden;
76
+ }
77
+
78
+ .value {
79
+ display: flex;
80
+ justify-content: center;
81
+ align-items: center;
82
+ position: absolute;
83
+ left: 0;
84
+ height: var(--ui3n-progress-linear-height);
85
+ border-radius: calc(var(--ui3n-progress-linear-height) - 2px);
86
+ background-color: var(--ui3n-progress-linear-color);
87
+ }
88
+
89
+ .text {
90
+ font-size: calc(var(--ui3n-progress-linear-height) - 2px);
91
+ font-weight: 500;
92
+ line-height: var(--ui3n-progress-linear-height);
93
+ color: var(--ui3n-progress-linear-bg);
94
+ }
95
+
96
+ .above {
97
+ position: relative;
98
+ width: 100%;
99
+ text-align: center;
100
+ font-size: var(--font-14);
101
+ line-height: var(--font-16);
102
+ color: var(--color-bg-control-accent-default);
103
+ }
104
+
105
+ .indeterminate {
106
+ .value {
107
+ animation: 1s linear 0s infinite normal move;
108
+ }
109
+ }
110
+
111
+ @keyframes move {
112
+ from {
113
+ left: -22%
114
+ }
115
+ to {
116
+ left: 122%
117
+ }
118
+ }
119
+ </style>