@v1nt1248/3nclient-lib 0.0.25 → 0.0.27

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.
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.0.25",
5
+ "version": "0.0.27",
6
6
  "description": "Library for 3NWeb clients",
7
7
  "private": false,
8
8
  "type": "module",
@@ -47,16 +47,16 @@
47
47
  "vue": "^3.3.4"
48
48
  },
49
49
  "devDependencies": {
50
- "@storybook/addon-actions": "^7.4.4",
51
- "@storybook/addon-docs": "^7.4.4",
52
- "@storybook/addon-essentials": "^7.4.4",
53
- "@storybook/addon-interactions": "^7.4.4",
54
- "@storybook/addon-links": "^7.4.4",
55
- "@storybook/blocks": "^7.4.4",
56
- "@storybook/builder-vite": "^7.4.4",
50
+ "@storybook/addon-actions": "^7.5.1",
51
+ "@storybook/addon-docs": "^7.5.1",
52
+ "@storybook/addon-essentials": "^7.5.1",
53
+ "@storybook/addon-interactions": "^7.5.1",
54
+ "@storybook/addon-links": "^7.5.1",
55
+ "@storybook/blocks": "^7.5.1",
56
+ "@storybook/builder-vite": "^7.5.1",
57
57
  "@storybook/testing-library": "^0.2.1",
58
- "@storybook/vue3": "^7.4.4",
59
- "@storybook/vue3-vite": "^7.4.4",
58
+ "@storybook/vue3": "^7.5.1",
59
+ "@storybook/vue3-vite": "^7.5.1",
60
60
  "@types/lodash": "^4.14.197",
61
61
  "@types/node": "^20.5.6",
62
62
  "@types/sanitize-html": "^2.9.0",
@@ -86,7 +86,7 @@
86
86
  "stylelint-rscss": "^0.4.0",
87
87
  "stylelint-scss": "^5.1.0",
88
88
  "typescript": "^5.2.2",
89
- "vite": "^4.4.5",
89
+ "vite": "^4.5.0",
90
90
  "vite-plugin-css-injected-by-js": "^3.3.0",
91
91
  "vite-plugin-dts": "^3.5.3",
92
92
  "vue": "^3.3.4",
@@ -10,6 +10,8 @@ import Ui3nInput from './ui3n-input.vue'
10
10
  import type { Ui3nInputProps, Ui3nInputEmits } from './ui3n-input.vue'
11
11
  import Ui3nText from './ui3n-text.vue'
12
12
  import type { Ui3nTextProps, Ui3nTextEmits } from './ui3n-text.vue'
13
+ import Ui3mCheckbox from './ui3n-checkbox.vue'
14
+ import type { Ui3nCheckboxProps, Ui3nCheckboxEmits, Ui3nCheckboxSlots } from './ui3n-checkbox.vue'
13
15
  import Ui3nNotification from './ui3n-notification.vue'
14
16
  import Ui3nTableSortIcon from './ui3n-table-sort-icon.vue'
15
17
  import type { Ui3nTableSortIconProps } from './ui3n-table-sort-icon.vue'
@@ -23,6 +25,8 @@ import Ui3nList from './ui3n-list.vue'
23
25
  import type { Ui3nListProps, Ui3nListEmits, Ui3nListSlots } from './ui3n-list.vue'
24
26
  import Ui3nVirtualScroll from './ui3n-virtual-scroll.vue'
25
27
  import type { Ui3nVirtualScrollProps, Ui3nVirtualScrollSlots } from './ui3n-virtual-scroll.vue'
28
+ import Ui3nTabs from './ui3n-tabs.vue'
29
+ import type { Ui3nTabsProps, Ui3nTabsEmits, Ui3nTabsSlots } from './ui3n-tabs.vue'
26
30
 
27
31
  export {
28
32
  Ui3nIcon,
@@ -43,6 +47,10 @@ export {
43
47
  Ui3nText,
44
48
  Ui3nTextProps,
45
49
  Ui3nTextEmits,
50
+ Ui3mCheckbox,
51
+ Ui3nCheckboxProps,
52
+ Ui3nCheckboxEmits,
53
+ Ui3nCheckboxSlots,
46
54
  Ui3nNotification,
47
55
  Ui3nTableSortIcon,
48
56
  Ui3nTableSortIconProps,
@@ -65,4 +73,8 @@ export {
65
73
  Ui3nVirtualScroll,
66
74
  Ui3nVirtualScrollProps,
67
75
  Ui3nVirtualScrollSlots,
76
+ Ui3nTabs,
77
+ Ui3nTabsProps,
78
+ Ui3nTabsEmits,
79
+ Ui3nTabsSlots,
68
80
  }
@@ -110,8 +110,7 @@
110
110
  line-height: var(--ui3n-button-text-height);
111
111
  font-weight: 600;
112
112
  color: var(--ui3n-button-text-color);
113
- background-position: center;
114
- transition: background 0.5s ease-in-out;
113
+ user-select: none;
115
114
 
116
115
  &--round {
117
116
  --ui3n-button-padding-vert: 6px;
@@ -125,13 +124,7 @@
125
124
  }
126
125
 
127
126
  &:not([disabled]) {
128
- cursor: pointer;
129
-
130
127
  &:hover {
131
- background:
132
- var(--ui3n-button-background)
133
- radial-gradient(circle, transparent 1%, var(--ui3n-button-background) 1%)
134
- center/15000%;
135
128
  box-shadow: 0 0 4px var(--shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12));
136
129
 
137
130
  .ui3n-button {
@@ -141,13 +134,6 @@
141
134
  }
142
135
  }
143
136
  }
144
-
145
- &:active {
146
- opacity: 1;
147
- background-color: var(--black-30, #b3b3b3);
148
- background-size: 100%;
149
- transition: background 0s;
150
- }
151
137
  }
152
138
 
153
139
  &__icon {
@@ -168,5 +154,16 @@
168
154
  &[disabled] {
169
155
  opacity: 0.7;
170
156
  }
157
+
158
+ &:not(.ui3n-button--round) {
159
+ .ui3n-button {
160
+ &__icon,
161
+ &__text {
162
+ pointer-events: none
163
+ }
164
+ }
165
+ }
166
+
167
+ @include ripple(var(--ui3n-button-background));
171
168
  }
172
169
  </style>
@@ -0,0 +1,191 @@
1
+ <script lang="ts" setup>
2
+ import { computed, onMounted, watch, onBeforeMount, ref, useSlots } from 'vue'
3
+ import Ui3nIcon from './ui3n-icon.vue'
4
+
5
+ type Ui3nCheckboxValue = boolean | string | number;
6
+
7
+ export interface Ui3nCheckboxProps {
8
+ modelValue: Ui3nCheckboxValue;
9
+ checkedValue?: Ui3nCheckboxValue;
10
+ uncheckedValue?: Ui3nCheckboxValue;
11
+ size?: number|string;
12
+ color?: string;
13
+ indeterminate?: boolean;
14
+ disabled?: boolean;
15
+ }
16
+
17
+ export interface Ui3nCheckboxEmits {
18
+ (ev: 'change', value: Ui3nCheckboxValue): void;
19
+ (ev: 'update:modelValue', value: Ui3nCheckboxValue): void;
20
+ }
21
+
22
+ export interface Ui3nCheckboxSlots {
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ default: () => any;
25
+ }
26
+
27
+ const iconMap = {
28
+ checked: 'check-box-checked',
29
+ unchecked: 'check-box',
30
+ indeterminate: 'check-box-inderterminate',
31
+ }
32
+
33
+ const props = withDefaults(
34
+ defineProps<Ui3nCheckboxProps>(),
35
+ {
36
+ modelValue: false,
37
+ checkedValue: true,
38
+ uncheckedValue: false,
39
+ size: '16',
40
+ color: '#0090ec',
41
+ indeterminate: false,
42
+ disabled: false,
43
+ },
44
+ )
45
+ const emits = defineEmits<Ui3nCheckboxEmits>()
46
+ defineSlots<Ui3nCheckboxSlots>()
47
+
48
+ const slots = useSlots()
49
+
50
+ const checkboxEl = ref<HTMLDivElement|null>(null)
51
+ const val = ref<Ui3nCheckboxValue>(props.uncheckedValue || false)
52
+ const uncertain = ref(false)
53
+
54
+ onBeforeMount(() => {
55
+ const checkedValueType = typeof props.checkedValue
56
+ const uncheckedValueType = typeof props.uncheckedValue
57
+ const valueType = typeof props.modelValue
58
+ if (
59
+ checkedValueType !== uncheckedValueType
60
+ || checkedValueType !== valueType
61
+ || uncheckedValueType !== valueType
62
+ ) {
63
+ throw Error('[Ui3nCheckbox] types of "value", "checkedValue" and "unchecktdValue" are different')
64
+ }
65
+ })
66
+
67
+ onMounted(() => {
68
+ if (checkboxEl.value) {
69
+ checkboxEl.value.style.setProperty('--ui3n-checkbox-size', `${props.size}px`)
70
+ }
71
+ })
72
+
73
+ watch(
74
+ () => props.modelValue,
75
+ newValue => val.value = newValue,
76
+ { immediate: true },
77
+ )
78
+
79
+ watch(
80
+ () => props.indeterminate,
81
+ newValue => uncertain.value = newValue,
82
+ { immediate: true},
83
+ )
84
+
85
+ watch(
86
+ () => props.size,
87
+ newValue => {
88
+ if (checkboxEl.value) {
89
+ checkboxEl.value.style.setProperty('--ui3n-checkbox-size', `${newValue}px`)
90
+ }
91
+ },
92
+ )
93
+
94
+ const checkBoxValue = computed(() => {
95
+ if (uncertain.value) {
96
+ return 'indeterminate'
97
+ }
98
+
99
+ return val.value === props.checkedValue
100
+ ? 'checked'
101
+ : 'unchecked'
102
+ } )
103
+
104
+ const change = () => {
105
+ val.value = checkBoxValue.value !== 'unchecked'
106
+ ? props.uncheckedValue
107
+ : props.checkedValue
108
+
109
+ if (uncertain.value) {
110
+ uncertain.value = false
111
+ }
112
+
113
+ emits('change', val.value)
114
+ emits('update:modelValue', val.value)
115
+ }
116
+ </script>
117
+
118
+ <template>
119
+ <div
120
+ ref="checkboxEl"
121
+ :class="[
122
+ 'ui3n-checkbox',
123
+ {
124
+ 'ui3n-checkbox--disabled': props.disabled,
125
+ 'ui3n-checkbox--with-label': slots.default,
126
+ },
127
+ ]"
128
+ >
129
+ <div
130
+ class="ui3n-checkbox__wrapper"
131
+ @click="change"
132
+ >
133
+ <ui3n-icon
134
+ class="ui3n-checkbox__icon"
135
+ :icon="iconMap[checkBoxValue]"
136
+ :width="props.size"
137
+ :height="props.size"
138
+ :color="props.color"
139
+ />
140
+ </div>
141
+
142
+ <div
143
+ class="ui3n-checkbox__label"
144
+ @click="change"
145
+ >
146
+ <slot />
147
+ </div>
148
+ </div>
149
+ </template>
150
+
151
+ <style lang="scss" scoped>
152
+ @import "../assets/styles/mixins";
153
+
154
+ .ui3n-checkbox {
155
+ --ui3n-checkbox-size: 16px;
156
+ --ui3n-checkbox-text-size: 12px;
157
+ --ui3n-checkbox-text-color: var(--black-90, #212121);
158
+ --ui3n-checkbox-text-weight: 500;
159
+
160
+ position: relative;
161
+ display: flex;
162
+ justify-content: flex-start;
163
+ align-items: center;
164
+
165
+ &__wrapper {
166
+ position: relative;
167
+ display: flex;
168
+ width: calc(var(--ui3n-checkbox-size) * 1.5);
169
+ height: calc(var(--ui3n-checkbox-size) * 1.5);
170
+ justify-content: center;
171
+ align-items: center;
172
+ border-radius: 50%;
173
+ cursor: pointer;
174
+
175
+ @include ripple(transparent);
176
+ }
177
+
178
+ &__label {
179
+ font-size: var(--ui3n-checkbox-text-size);
180
+ font-weight: var(--ui3n-checkbox-text-weight);
181
+ color: var(--ui3n-checkbox-text-color);
182
+ user-select: none;
183
+ cursor: pointer;
184
+ }
185
+
186
+ &--disabled {
187
+ pointer-events: none;
188
+ opacity: 0.5;
189
+ }
190
+ }
191
+ </style>
@@ -0,0 +1,174 @@
1
+ <script lang="ts" setup>
2
+ import { computed, onMounted, ref, watch } from 'vue'
3
+
4
+ export interface Ui3nTabsProps {
5
+ modelValue: number;
6
+ itemDirection?: 'horizontal' | 'vertical';
7
+ activeColor?: string;
8
+ inactiveColor?: string;
9
+ indicatorColor?: string;
10
+ indicatorSize?: number | string;
11
+ indicatorPosition?: 'normal' | 'reverse';
12
+ }
13
+
14
+ export interface Ui3nTabsEmits {
15
+ (ev: 'update:modelValue', val: number): void;
16
+ }
17
+
18
+ export interface Ui3nTabsSlots {
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ default: () => any;
21
+ }
22
+
23
+ const props = withDefaults(
24
+ defineProps<Ui3nTabsProps>(),
25
+ {
26
+ modelValue: 0,
27
+ itemDirection: 'horizontal',
28
+ activeColor: 'var(--blue-main, #0090ec)',
29
+ inactiveColor: 'var(--black-90, #212121)',
30
+ indicatorColor: 'var(--blue-main, #0090ec)',
31
+ indicatorSize: 2,
32
+ indicatorPosition: 'normal',
33
+ },
34
+ )
35
+ const emits = defineEmits<Ui3nTabsEmits>()
36
+
37
+
38
+ const tabs = ref<HTMLDivElement | null>(null)
39
+ const children = ref<HTMLCollection | null>(null)
40
+
41
+ const activeColor = computed(() => props.activeColor)
42
+ const inactiveColor = computed(() => props.inactiveColor)
43
+ const indicatorColor = computed(() => props.indicatorColor)
44
+ const indicatorSize = computed(() => `${props.indicatorSize}px`)
45
+ const indicatorPosition = computed(() => props.indicatorPosition === 'reverse' ? '100%' : '0')
46
+
47
+ watch(
48
+ () => props.modelValue,
49
+ newValue => setTabsActivity(newValue),
50
+ )
51
+
52
+ onMounted(() => {
53
+ if (tabs.value) {
54
+ children.value = tabs.value.children
55
+ initialTabsActivity()
56
+ setTabsActivity(props.modelValue)
57
+ }
58
+ })
59
+
60
+ function initialTabsActivity(): void {
61
+ if (children.value) {
62
+ for (let i = 0; i < children.value.length; i++) {
63
+ // @ts-ignore
64
+ children.value[i].dataset.index = `${i}`
65
+ children.value[i].classList.add('ui3n-tabs__item')
66
+ }
67
+ }
68
+ }
69
+
70
+ function setTabsActivity(index: number): void {
71
+ if (children.value) {
72
+ for (let i = 0; i < children.value.length; i++) {
73
+ if (i === index) {
74
+ children.value[i].classList.add('ui3n-tabs__item--active')
75
+ } else {
76
+ children.value[i].classList.remove('ui3n-tabs__item--active')
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ const onClick = (ev: MouseEvent) => {
83
+ // @ts-ignore
84
+ const { index } = ev.target.dataset
85
+ if (index) {
86
+ emits('update:modelValue', Number(index))
87
+ }
88
+ }
89
+ </script>
90
+
91
+ <template>
92
+ <div
93
+ ref="tabs"
94
+ :class="[
95
+ 'ui3n-tabs',
96
+ props.itemDirection === 'vertical' && 'ui3n-tabs--vertical',
97
+ ]"
98
+ v-on="children ? { click: onClick } : {}"
99
+ >
100
+ <slot />
101
+ </div>
102
+ </template>
103
+
104
+ <style lang="scss" scoped>
105
+ @import "../assets/styles/mixins";
106
+
107
+ .ui3n-tabs {
108
+ --ui3n-tabs-height: 48px;
109
+
110
+ position: relative;
111
+ height: var(--ui3n-tabs-height);
112
+ display: flex;
113
+ justify-content: center;
114
+ align-items: stretch;
115
+
116
+ :deep(.ui3n-tabs__item) {
117
+ cursor: pointer;
118
+
119
+ &.ui3n-tabs__item {
120
+ position: relative;
121
+ user-select: none;
122
+ color: v-bind('inactiveColor');
123
+
124
+ &:hover {
125
+ background-color: var(--blue-main-20, #d8edfd);
126
+ }
127
+
128
+ &::after {
129
+ position: absolute;
130
+ content: '';
131
+ left: 0;
132
+ width: 100%;
133
+ bottom: v-bind('indicatorPosition');
134
+ height: v-bind('indicatorSize');
135
+ background-color: transparent;
136
+ transition: background-color 250ms ease-in-out;
137
+ }
138
+
139
+ &[disabled] {
140
+ opacity: 0.5;
141
+ cursor: default;
142
+ pointer-events: none
143
+ }
144
+
145
+ @include ripple(#d8edfd);
146
+ }
147
+
148
+ &.ui3n-tabs__item--active {
149
+ color: v-bind('activeColor');
150
+
151
+ &::after {
152
+ background-color: v-bind('indicatorColor');
153
+ transition: background-color 250ms ease-in-out;
154
+ }
155
+ }
156
+ }
157
+
158
+ &--vertical {
159
+ height: auto;
160
+ flex-direction: column;
161
+
162
+ :deep(.ui3n-tabs__item) {
163
+ &.ui3n-tabs__item {
164
+ &::after {
165
+ left: v-bind('indicatorPosition');
166
+ width: v-bind('indicatorSize');
167
+ bottom: 0;
168
+ height: 100%;
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ </style>