fu-kit 0.5.1 → 0.6.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.
@@ -0,0 +1,206 @@
1
+ <template>
2
+ <label
3
+ class="ui-check"
4
+ :class="{'_disabled': isDisabled, '_checked': modelValue }"
5
+ v-bind="{ class: $attrs.class, style: $attrs.style }"
6
+ @mouseup="mouseUp"
7
+ >
8
+ <input
9
+ v-bind="{...$attrs, disabled: isDisabled, type: 'checkbox', class: undefined, style: undefined}"
10
+ class="ui-check_input"
11
+ @input="$emit('update:modelValue', $event.target.checked)"
12
+ :checked="modelValue"
13
+ ref="inputRef"
14
+ >
15
+ <span
16
+ class="ui-check_box"
17
+ :class="{
18
+ 'ui-check_switch': switchLike,
19
+ 'ui-check_check': !switchLike,
20
+ _loading: isLoading
21
+ }"
22
+ >
23
+ <svg v-if="!switchLike" class="ui-check_check-icon" viewBox="0 0 24 24">
24
+ <path stroke="var(--ui-pal, currentColor)" stroke-linecap="round" stroke-width="2" d="m7.5 12.5 4 3 5.5-8" />
25
+ </svg>
26
+ </span>
27
+ <slot />
28
+ </label>
29
+ </template>
30
+
31
+ <script>
32
+ import { computed, defineComponent, ref } from 'vue'
33
+
34
+ export default defineComponent({
35
+ name: 'ui-check',
36
+ props: {
37
+ modelValue: { type: Boolean, default: false },
38
+ switchLike: { type: Boolean, default: false },
39
+ thin: { type: Boolean, default: false },
40
+ isLoading: { type: Boolean, default: false },
41
+ disabled: { type: Boolean, default: false },
42
+ },
43
+ emits: [ 'update:modelValue' ],
44
+ setup (props) {
45
+ const inputRef = ref(null)
46
+ const isDisabled = computed(() => props.disabled || props.disabled === '')
47
+
48
+
49
+ const mouseUp = () => {
50
+ // oof
51
+ setTimeout(() => {
52
+ inputRef.value.blur()
53
+ }, 0)
54
+ }
55
+
56
+ return { isDisabled, inputRef, mouseUp }
57
+ },
58
+ })
59
+ </script>
60
+ <style lang="scss" scoped>
61
+ @import "../../scss";
62
+
63
+ .ui-check {
64
+ @include typo(200);
65
+
66
+ user-select: none;
67
+ display: flex;
68
+ box-sizing: border-box;
69
+ align-items: center;
70
+ justify-content: var(--ui-check-justify-content, stretch);
71
+ height: var(--ui-lt-h);
72
+ position: relative;
73
+ outline: none;
74
+ color: var(--pal-text-dimm);
75
+ font-family: var(--typo-font-text);
76
+ cursor: pointer;
77
+ line-height: 1;
78
+
79
+ &_input {
80
+ opacity: 0; // weird, but it's working inside the label
81
+ position: absolute;
82
+ left: 0;
83
+ top: 0;
84
+ pointer-events: none;
85
+ }
86
+
87
+ &_check {
88
+ border-style: var(--ui-lt-border-style);
89
+ border-width: var(--ui-check-border-w);
90
+ border-color: var(--pal-grey700);
91
+ height: var(--ui-check-size);
92
+ width: var(--ui-check-size);
93
+ border-radius: var(--ui-lt-border-radius);
94
+ display: flex;
95
+ justify-content: stretch;
96
+ align-items: stretch;
97
+ margin-right: spacing(200);
98
+ transition: all var(--ui-transition);
99
+
100
+ &-icon {
101
+ transition: all var(--ui-transition);
102
+ transform: scale(0);
103
+ width: 100%;
104
+ }
105
+ }
106
+
107
+ &._checked &_check {
108
+ border-color: var(--ui-pal);
109
+ }
110
+
111
+ &._checked &_check-icon {
112
+ transform: scale(1.4);
113
+ }
114
+
115
+ &_switch {
116
+ width: var(--ui-switch-w);
117
+ border-radius: var(--ui-switch-h);
118
+ background-color: var(--pal-grey700);
119
+ border: var(--ui-lt-border-width) solid var(--pal-grey700);
120
+ margin-right: spacing(200);
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: flex-start;
124
+ height: var(--ui-switch-h);
125
+
126
+ --switch-left: 0;
127
+
128
+ &:before {
129
+ transform: translateX(var(--switch-left));
130
+ content: "";
131
+ display: block;
132
+ background-color: white;
133
+ border: var(--ui-lt-border-width) solid transparent;
134
+ border-radius: var(--ui-switch-h);
135
+ height: calc(100% - var(--ui-lt-border-width) * 2);
136
+ aspect-ratio: 1;
137
+ transition: all var(--ui-transition);
138
+ }
139
+ }
140
+
141
+ &._checked &_switch {
142
+ background-color: var(--ui-pal);
143
+ border-color: var(--ui-pal);
144
+ }
145
+
146
+ &._checked &_switch:before {
147
+ --switch-left: calc(var(--ui-switch-w) - var(--ui-switch-h));
148
+ transform: translateX(var(--switch-left));
149
+ }
150
+
151
+ &._checked {
152
+ color: var(--pal-text);
153
+ }
154
+
155
+ &:hover:not(&._disabled) &_box {
156
+ box-shadow: 0 5px 12px -4px rgb(var(--rgb-dark), 0.2);
157
+ }
158
+
159
+ &:hover {
160
+ color: var(--pal-text);
161
+ }
162
+
163
+ &:focus-within &_box {
164
+ box-shadow: 0 0 2px 4px rgba(var(--ui-rgb), 0.3);
165
+ }
166
+
167
+ &._disabled {
168
+ color: var(--ui-pal-disabled-border);
169
+ cursor: not-allowed;
170
+ }
171
+
172
+ &._disabled &_check {
173
+ border: var(--ui-lt-border-width) var(--ui-lt-disabled-border-style) var(--ui-pal-disabled-border);
174
+ box-shadow: none;
175
+ }
176
+
177
+ &._disabled &_switch {
178
+ background-color: rgba(var(--rgb-grey700), 0.25);
179
+ border-color: transparent;
180
+ }
181
+
182
+ &._disabled._checked &_switch {
183
+ background-color: rgba(var(--ui-rgb), 0.3);
184
+ }
185
+ }
186
+
187
+ ._loading {
188
+ animation: pulse 1000ms infinite;
189
+ }
190
+
191
+ @keyframes pulse {
192
+ 40% {
193
+ transform: scale(1.1);
194
+ box-shadow: 0 0 0 5px rgba(var(--ui-rgb), 0.3);
195
+ }
196
+
197
+ 80% {
198
+ transform: scale(1);
199
+ box-shadow: 0 0 0 10px rgba(var(--ui-rgb), 0);
200
+ }
201
+
202
+ 100% {
203
+ box-shadow: 0 0 0 0 rgba(var(--ui-rgb), 0)
204
+ }
205
+ }
206
+ </style>
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <div class="code-input">
3
+ <div class="code-input_wrapper">
4
+ <input
5
+ :maxlength="$props.length"
6
+ type="text"
7
+ class="code-input_inp"
8
+ :value="$props.modelValue"
9
+ @input="handleInput"
10
+ ref="inp"
11
+ @focus.passive="selectionUpdate"
12
+ @dragend.passive="selectionUpdate"
13
+ @mouseup.passive="selectionUpdate"
14
+ @keydown.passive="selectionUpdate"
15
+ @keyup.passive="selectionUpdate"
16
+ v-bind="{ ...$attrs }"
17
+ />
18
+ <div
19
+ v-for="i in $props.length"
20
+ :key="'char' + i"
21
+ class="code-input_char"
22
+ :class="{
23
+ '_select': i - 1 >= selection.from && i - 1 < selection.to && selection.from !== selection.to,
24
+ '_cursor': i - 1 === selection.to,
25
+ }"
26
+ >
27
+ <span class="code-input_char-item">{{ chars[i - 1] || '' }}</span>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
35
+
36
+ export default {
37
+ name: 'ui-code-input',
38
+ props: {
39
+ modelValue: {
40
+ type: String,
41
+ },
42
+ length: {
43
+ type: Number,
44
+ default: 6,
45
+ },
46
+ },
47
+ setup (props, { emit }) {
48
+ const selection = reactive({ from: 0, to: 0 })
49
+ const inp = ref(null)
50
+
51
+ const selectionUpdate = (e) => {
52
+ selection.from = e.target.selectionStart
53
+ selection.to = e.target.selectionEnd
54
+ }
55
+
56
+ const handleInput = (e) => {
57
+ emit('update:modelValue', e.target.value)
58
+ }
59
+
60
+ const chars = computed(() => (props.modelValue).split(''))
61
+
62
+ onMounted(() => { })
63
+ onBeforeUnmount(() => { })
64
+
65
+ return { chars, selection, selectionUpdate, inp, handleInput }
66
+ },
67
+ }
68
+ </script>
69
+
70
+ <style lang="scss" scoped>
71
+ .code-input {
72
+ --charbox-width: 34px;
73
+ --chabox-height: 48px;
74
+
75
+ display: flex;
76
+
77
+ &_wrapper {
78
+ position: relative;
79
+ display: flex;
80
+ justify-content: flex-start;
81
+ align-items: center;
82
+ flex-wrap: nowrap;
83
+ gap: spacing(400);
84
+ }
85
+
86
+ &_inp {
87
+ padding: 14px;
88
+ position: absolute;
89
+ width: 100%;
90
+ border: none;
91
+ background: transparent;
92
+ font-family: var(--typo-font-mono);
93
+ font-size: var(--typo-h200);
94
+ letter-spacing: 41px;
95
+ outline: none;
96
+ opacity: 0;
97
+ }
98
+
99
+ &_char {
100
+ width: var(--charbox-width);
101
+ height: var(--ui-lt-h);
102
+ border: 1px solid var(--charbox-border-color, var(--pal-grey300));
103
+ border-radius: var(--ui-lt-border-radius);
104
+ color: var(--charbox-text-color, var(--pal-grey900));
105
+ align-items: center;
106
+ justify-content: center;
107
+ display: flex;
108
+
109
+ &-item {
110
+ border-width: 2px 0;
111
+ border-style: solid;
112
+ border-color: transparent;
113
+ background: transparent;
114
+ min-width: 0.75em;
115
+ min-height: 1.5em;
116
+ text-align: center;
117
+ }
118
+
119
+ &._select &-item {
120
+ color: var(--pal-white);
121
+ background: var(--pal-primary);
122
+ }
123
+
124
+ input:focus ~ &._cursor {
125
+ border-color: var(--ui-pal);
126
+ }
127
+
128
+ input:focus ~ &._cursor &-item {
129
+ border-bottom-color: var(--pal-black);
130
+ }
131
+ }
132
+ }
133
+ </style>
@@ -1,63 +1,63 @@
1
- <template>
2
- <div class="fu-code-view">
3
- <fu-button-link
4
- v-if="collapse"
5
- class="fu-code-view_toggle"
6
- @click="isShown = !isShown"
7
- >
8
- {{ isShown ? 'Hide' : 'Show' }} {{ label }}
9
- </fu-button-link>
10
- <p class="fu-code-view_title" v-else-if="label">{{ label }}</p>
11
- <pre v-if="collapse ? isShown : true" class="fu-code-view_pre"><slot /></pre>
12
- </div>
13
- </template>
14
-
15
- <script>
16
- import { ref, toRaw } from 'vue'
17
-
18
- import FuButtonLink from './FuButtonLink.vue'
19
-
20
- export default {
21
- name: 'fu-code-view',
22
- components: { FuButtonLink },
23
- props: {
24
- label: { type: String, default: '' },
25
- collapse: { type: Boolean, default: null },
26
- },
27
- setup (props) {
28
- const collapse = ref(toRaw(props.collapse))
29
- const isShown = ref(false)
30
- const label = props.label
31
- return { isShown, label, collapse }
32
- },
33
- }
34
- </script>
35
-
36
- <style scoped lang="scss">
37
- @import "../../scss";
38
-
39
- .fu-code-view {
40
- @include spacing-margin(200, 0);
41
-
42
- &_title {
43
- @include typo(100);
44
-
45
- font-weight: bold;
46
- margin-bottom: spacing(200);
47
- }
48
-
49
- &_pre {
50
- @include scrollbar-awesome();
51
- @include spacing-padding(300, 200);
52
-
53
- font-family: var(--typo-font-mono);
54
- overflow: auto;
55
- max-width: 100%;
56
- background: var(--pal-block);
57
- border: 1px solid var(--pal-block-border);
58
- border-radius: var(--ui-lt-border-radius);
59
-
60
- --ui-scroll-bg: var(--pal-block);
61
- }
62
- }
1
+ <template>
2
+ <div class="ui-code-view">
3
+ <ui-button-link
4
+ v-if="collapse"
5
+ class="ui-code-view_toggle"
6
+ @click="isShown = !isShown"
7
+ >
8
+ {{ isShown ? 'Hide' : 'Show' }} {{ label }}
9
+ </ui-button-link>
10
+ <p class="ui-code-view_title" v-else-if="label">{{ label }}</p>
11
+ <pre v-if="collapse ? isShown : true" class="ui-code-view_pre"><slot /></pre>
12
+ </div>
13
+ </template>
14
+
15
+ <script>
16
+ import { defineComponent, ref, toRaw } from 'vue'
17
+
18
+ import UiButtonLink from './UiButtonLink.vue'
19
+
20
+ export default defineComponent({
21
+ name: 'ui-code-view',
22
+ components: { UiButtonLink },
23
+ props: {
24
+ label: { type: String, default: '' },
25
+ collapse: { type: Boolean, default: null },
26
+ },
27
+ setup (props) {
28
+ const collapse = ref(toRaw(props.collapse))
29
+ const isShown = ref(false)
30
+ const label = props.label
31
+ return { isShown, label, collapse }
32
+ },
33
+ })
34
+ </script>
35
+
36
+ <style scoped lang="scss">
37
+ @import "../../scss";
38
+
39
+ .ui-code-view {
40
+ margin: spacing(200, 0);
41
+
42
+ &_title {
43
+ @include typo(100);
44
+
45
+ font-weight: bold;
46
+ margin-bottom: spacing(200);
47
+ }
48
+
49
+ &_pre {
50
+ @include scrollbar-awesome();
51
+ padding: spacing(300, 200);
52
+
53
+ font-family: var(--typo-font-mono);
54
+ overflow: auto;
55
+ max-width: 100%;
56
+ background: var(--pal-block);
57
+ border: 1px solid var(--pal-block-border);
58
+ border-radius: var(--ui-lt-border-radius);
59
+
60
+ --ui-scroll-bg: var(--pal-block);
61
+ }
62
+ }
63
63
  </style>
@@ -1,24 +1,24 @@
1
1
  <template>
2
- <fu-button
3
- hollow
2
+ <ui-button
3
+ :hollow="!isCopied"
4
4
  v-bind="$attrs"
5
- class="fu-copy"
5
+ class="ui-copy"
6
6
  @click="handleCopyToClipboard"
7
7
  :class="{'_hot': isCopied}"
8
8
  >
9
9
  <slot />
10
- </fu-button>
10
+ </ui-button>
11
11
  </template>
12
12
 
13
13
  <script>
14
- import { onBeforeUnmount, ref } from 'vue'
15
- import FuButton from './FuButton.vue'
14
+ import { defineComponent, onBeforeUnmount, ref } from 'vue'
15
+ import UiButton from './UiButton.vue'
16
16
 
17
- export default {
18
- name: 'fu-copy',
19
- components: { FuButton },
17
+ export default defineComponent({
18
+ name: 'ui-copy',
19
+ components: { UiButton },
20
20
  props: {
21
- value: { type: String, required: true },
21
+ value: { type: [ String, null ], required: true },
22
22
  },
23
23
  setup (props) {
24
24
  const isCopied = ref(false)
@@ -68,42 +68,21 @@ export default {
68
68
  isCopied,
69
69
  }
70
70
  },
71
- }
71
+ })
72
72
  </script>
73
73
 
74
74
  <style lang="scss" scoped>
75
75
  @import "../../scss";
76
76
 
77
- .fu-copy {
78
- @include spacing-padding(0, 200);
77
+ .ui-copy {
78
+ padding: spacing(0, 200);
79
79
  @include ellipsis();
80
80
 
81
- // todo: replace on vanilla button to get rid of !important maybe?
82
81
  --ui-lt-h: 2em;
83
- --ui-pal: var(--pal-block-border);
84
- color: var(--pal-text) !important;
85
- overflow: hidden;
86
-
87
- &._hollow {
88
- font-weight: normal;
89
-
90
- &:active {
91
- box-shadow: none;
92
- }
93
-
94
- &:hover {
95
- box-shadow: none;
96
- }
97
-
98
- &:focus {
99
- box-shadow: none;
100
- }
82
+ --ui-pal: var(--pal-light);
101
83
 
102
- &._hot {
103
- transition: none;
104
- border-color: var(--pal-positive);
105
- }
84
+ &._hot {
85
+ color: var(--pal-positive);
106
86
  }
107
-
108
87
  }
109
88
  </style>
@@ -1,14 +1,14 @@
1
1
  <template>
2
2
  <div
3
- class="fu-dropdown"
3
+ class="ui-dropdown"
4
4
  :class="{ '_is-open': !disabled && isOpen }"
5
5
  v-click-away="isOpen && handleClickaway"
6
6
  >
7
- <div class="fu-dropdown_trigger" @click="toggle">
8
- <slot />
7
+ <div class="ui-dropdown_trigger" @click="toggle">
8
+ <slot :isOpen="!disabled && isOpen" />
9
9
  </div>
10
10
 
11
- <div v-if="!disabled && isOpen" class="fu-dropdown_content" ref="slotWrap">
11
+ <div v-if="!disabled && isOpen" class="ui-dropdown_content" :class="{ _right: snapToRight }" ref="slotWrap">
12
12
  <slot name="content" :dropdownClose="close" />
13
13
  </div>
14
14
  </div>
@@ -19,11 +19,14 @@
19
19
 
20
20
  import { directive as clickAway } from 'vue3-click-away'
21
21
 
22
- export default {
23
- name: 'fu-dropdown',
22
+ import { defineComponent } from 'vue'
23
+
24
+ export default defineComponent({
25
+ name: 'ui-dropdown',
24
26
  directives: { clickAway },
25
27
  props: {
26
28
  clickaway: { type: Boolean, default: true },
29
+ snapToRight: { type: Boolean, default: false },
27
30
  disabled: { type: Boolean, default: false },
28
31
  },
29
32
  emits: [ 'open', 'close' ],
@@ -57,13 +60,13 @@ export default {
57
60
  this.isOpen = false
58
61
  },
59
62
  },
60
- }
63
+ })
61
64
  </script>
62
65
 
63
66
  <style lang="scss" scoped>
64
67
  @import "../../scss";
65
68
 
66
- .fu-dropdown {
69
+ .ui-dropdown {
67
70
  @include typo(200);
68
71
 
69
72
  padding: 0;
@@ -78,24 +81,29 @@ export default {
78
81
 
79
82
  &_content {
80
83
  @include scrollbar-awesome();
81
- @include spacing-padding(300, 0);
82
84
 
83
85
  overflow: auto;
84
- max-height: 30vh;
86
+ min-width: 100%;
85
87
  max-width: 100vw;
88
+ max-height: var(--ui-dropdown-max-height);
86
89
  flex-direction: column;
87
90
  justify-content: stretch;
88
- border-width: var(--ui-lt-border-width);
89
91
  border-radius: var(--ui-lt-border-radius);
90
- border-color: var(--ui-pal);
91
- border-style: solid;
92
92
  position: absolute;
93
- left: 0;
94
93
  top: 100%;
95
- min-width: 100%;
96
94
  background: var(--ui-pal-bg);
97
95
  margin-top: spacing(200);
98
96
  z-index: var(--lt-z-pop);
97
+ background: var(--pal-white);
98
+ box-shadow: 0 3px 11px rgba(var(--rgb-black), 0.2);
99
+
100
+ &:not(._right) {
101
+ left: 0;
102
+ }
103
+
104
+ &._right {
105
+ right: 0;
106
+ }
99
107
  }
100
108
  }
101
109
  </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="fu-dropdown-item"
3
+ class="ui-dropdown-item"
4
4
  :class="{
5
5
  '_interactive': interactive || autoClose,
6
6
  '_active': active,
@@ -12,8 +12,10 @@
12
12
  </template>
13
13
 
14
14
  <script>
15
- export default {
16
- name: 'fu-dropdown-item',
15
+ import { defineComponent } from 'vue'
16
+
17
+ export default defineComponent({
18
+ name: 'ui-dropdown-item',
17
19
  props: {
18
20
  autoClose: { type: Boolean, default: false },
19
21
  interactive: { type: Boolean, default: false },
@@ -27,32 +29,24 @@ export default {
27
29
  }
28
30
  },
29
31
  },
30
- }
32
+ })
31
33
  </script>
32
34
 
33
35
  <style lang="scss" scoped>
34
36
  @import "../../scss";
35
37
 
36
- .fu-dropdown-item {
37
- &:first-child {
38
- margin-top: spacing(200);
39
- }
40
-
41
- &:last-child {
42
- margin-bottom: spacing(200);
43
- }
44
-
45
- padding: spacing(200) calc(#{spacing(200)} + 1px);
38
+ .ui-dropdown-item {
39
+ padding: spacing(200) spacing(400);
46
40
  display: flex;
47
41
  align-items: center;
42
+ min-height: var(--ui-lt-h);
48
43
 
49
44
  &._interactive {
50
45
  color: var(--ui-pal-text);
51
46
  cursor: pointer;
52
47
 
53
48
  &:hover {
54
- color: var(--ui-pal-acc);
55
- background-color: var(--ui-pal);
49
+ color: var(--ui-pal);
56
50
  }
57
51
 
58
52
  &:focus,
@@ -68,10 +62,5 @@ export default {
68
62
  &:hover {
69
63
  }
70
64
  }
71
-
72
- &._selected {
73
- color: black;
74
- background-color: orange;
75
- }
76
65
  }
77
66
  </style>