@toife/vue 3.0.5 → 3.0.7

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 (31) hide show
  1. package/package.json +3 -4
  2. package/src/components/action/action.composable.ts +0 -1
  3. package/src/components/avatar/avatar.scss +9 -1
  4. package/src/components/avatar/avatar.type.ts +2 -0
  5. package/src/components/avatar/avatar.vue +11 -3
  6. package/src/components/button/button.scss +3 -1
  7. package/src/components/card/card/card.vue +1 -1
  8. package/src/components/checkbox/checkbox.scss +28 -16
  9. package/src/components/checkbox/checkbox.vue +2 -2
  10. package/src/components/decision-modal/decision-modal.vue +3 -5
  11. package/src/components/dropdown/dropdown.html +6 -0
  12. package/src/components/dropdown/dropdown.scss +74 -0
  13. package/src/components/dropdown/dropdown.type.ts +19 -0
  14. package/src/components/dropdown/dropdown.vue +109 -0
  15. package/src/components/dropdown/index.ts +2 -0
  16. package/src/components/field/field.html +12 -1
  17. package/src/components/field/outline/outline.html +29 -25
  18. package/src/components/field/outline/outline.scss +14 -21
  19. package/src/components/gesture-indicator/gesture-indicator.type.ts +1 -0
  20. package/src/components/gesture-indicator/gesture-indicator.vue +3 -1
  21. package/src/components/index.ts +2 -0
  22. package/src/components/radio/radio/radio.scss +23 -18
  23. package/src/components/radio/radio/radio.vue +9 -23
  24. package/src/components/select/index.ts +2 -0
  25. package/src/components/select/select.html +26 -0
  26. package/src/components/select/select.scss +58 -0
  27. package/src/components/select/select.type.ts +32 -0
  28. package/src/components/select/select.vue +89 -0
  29. package/src/components/switch/switch.scss +16 -14
  30. package/src/components/switch/switch.vue +2 -2
  31. package/src/factory.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toife/vue",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "A Frontend framework for Vue",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -31,7 +31,6 @@
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@toife/gesture": "^1.1.4",
34
- "@toife/sass-layer-generator": "^1.0.9",
35
34
  "vue": "^3.5.0",
36
35
  "vue-router": "^4.0.0"
37
36
  },
@@ -48,7 +47,6 @@
48
47
  "devDependencies": {
49
48
  "@commitlint/cli": "^20.4.4",
50
49
  "@commitlint/config-conventional": "^20.4.4",
51
- "@toife/sass-layer-generator": "^1.0.9",
52
50
  "@types/node": "^25.0.3",
53
51
  "@vitejs/plugin-vue": "^6.0.1",
54
52
  "husky": "^9.1.7",
@@ -62,6 +60,7 @@
62
60
  },
63
61
  "packageManager": "yarn@4.12.0",
64
62
  "dependencies": {
65
- "@toife/gesture": "^1.1.4"
63
+ "@toife/gesture": "^1.1.4",
64
+ "@toife/sass-layer-generator": "^1.1.0"
66
65
  }
67
66
  }
@@ -7,7 +7,6 @@ const visible = ref(false);
7
7
  export const useAction = () => {
8
8
  const open = (props: ActionComposableProps) => {
9
9
  data.value = props;
10
- // Defer showing one frame so Present/transition hooks see a clean open cycle
11
10
  setTimeout(() => {
12
11
  visible.value = true;
13
12
  }, 50);
@@ -6,10 +6,13 @@ $shape-pill: sass.fn-naming-prefix("shape-pill");
6
6
 
7
7
  // Property - layer: avatar
8
8
  $border-radius: sass.fn-naming-var("border-radius");
9
- $avatar-background-color: sass.fn-naming-var("avatar", "background-color");
9
+ $border-width: sass.fn-naming-var("stroke", "width");
10
10
  $width: sass.fn-naming-var("width");
11
11
  $transition-duration: sass.fn-naming-var("motion", "duration");
12
12
 
13
+ $avatar-background-color: sass.fn-naming-var("avatar", "background-color");
14
+ $avatar-border-color: sass.fn-naming-var("avatar", "border-color");
15
+
13
16
  // Avatar default is circle
14
17
  // With props square then is square
15
18
  .#{$avatar} {
@@ -28,8 +31,13 @@ $transition-duration: sass.fn-naming-var("motion", "duration");
28
31
  box-shadow #{$transition-duration} ease,
29
32
  border-radius #{$transition-duration} ease;
30
33
  display: inline-block;
34
+ border: #{$border-width} solid transparent;
31
35
 
32
36
  &.#{$shape-pill} {
33
37
  border-radius: 50%;
34
38
  }
39
+
40
+ &.divider {
41
+ border: #{$border-width} solid rgb(#{$avatar-border-color});
42
+ }
35
43
  }
@@ -3,4 +3,6 @@ export type AvatarProps = {
3
3
  shape?: string;
4
4
  size?: string | number;
5
5
  src?: string;
6
+ role?: string;
7
+ divider?: boolean;
6
8
  };
@@ -11,6 +11,8 @@ import { type AppProviderState, APP_PROVIDER_STATE_KEY } from "../app";
11
11
  const props = withDefaults(defineProps<AvatarProps>(), {
12
12
  size: "22px",
13
13
  src: "",
14
+ role: undefined,
15
+ divider: undefined,
14
16
  });
15
17
  const appState = inject<AppProviderState>(APP_PROVIDER_STATE_KEY);
16
18
 
@@ -18,7 +20,10 @@ const appState = inject<AppProviderState>(APP_PROVIDER_STATE_KEY);
18
20
  // ----------------------------------------------------------------------------
19
21
  const avatarAttrs = computed(() => {
20
22
  const shape = props?.shape || appState?.shape.value || "";
21
- const role = appState?.role.value || "";
23
+ const role = props?.role || appState?.role.value || "";
24
+ const divider = (props?.divider !== undefined ? props.divider : appState?.divider.value) ?? false;
25
+ const size = props.size + (typeof props.size === "number" ? "px" : "");
26
+ const backgroundImage = `url(${props.src})`;
22
27
 
23
28
  return {
24
29
  class: [
@@ -26,10 +31,13 @@ const avatarAttrs = computed(() => {
26
31
  withPrefix(["role", role]),
27
32
  withPrefix(["shape", shape]),
28
33
  withPrefix("avatar"),
34
+ {
35
+ divider: divider,
36
+ },
29
37
  ],
30
38
  style: {
31
- [property("width")]: props.size + (typeof props.size === "number" ? "px" : ""),
32
- backgroundImage: `url(${props.src})`,
39
+ [property("width")]: size,
40
+ backgroundImage: backgroundImage,
33
41
  },
34
42
  };
35
43
  });
@@ -25,6 +25,8 @@ $button-background-color-focus: sass.fn-naming-var("button", "background-color",
25
25
  $button-background-color-active: sass.fn-naming-var("button", "background-color", "active");
26
26
  $button-background-color-disabled: sass.fn-naming-var("button", "background-color", "disabled");
27
27
 
28
+ $button-box-shadow-color-focus: sass.fn-naming-var("button", "box-shadow-color", "focus");
29
+
28
30
  $size-font-size: sass.fn-naming-var("font-size");
29
31
  $size-height: sass.fn-naming-var("height");
30
32
  $size-coefficient-x: sass.fn-naming-var("coefficient-x");
@@ -69,7 +71,7 @@ $size-line-height: sass.fn-naming-var("line-height");
69
71
  background-color: rgb(#{$button-background-color-focus});
70
72
 
71
73
  &.shadow {
72
- box-shadow: 0 0 0 0.25rem rgb(#{$button-border-color}, 0.25);
74
+ box-shadow: 0 0 0 0.25rem rgb(#{$button-box-shadow-color-focus}, 0.25);
73
75
  }
74
76
  }
75
77
 
@@ -46,7 +46,7 @@ const cardAttrs = computed(() => {
46
46
 
47
47
  const cardProviderState = computed(() => {
48
48
  return {
49
- role: props.role,
49
+ role: role.value,
50
50
  shape: shape.value,
51
51
  divider: divider.value,
52
52
  };
@@ -7,16 +7,25 @@ $shape-rounded: sass.fn-naming-prefix("shape-rounded");
7
7
  $shape-pill: sass.fn-naming-prefix("shape-pill");
8
8
 
9
9
  // Properties - layer: item
10
- $checkbox-background-color: sass.fn-naming-var("checkbox", "background-color");
11
10
  $checkbox-background-color-hover: sass.fn-naming-var("checkbox", "background-color", "hover");
12
- $checkbox-background-color-inactive: sass.fn-naming-var("checkbox", "background-color", "inactive");
13
- $checkbox-background-color-contrast: sass.fn-naming-var("checkbox", "background-color", "contrast");
11
+ $checkbox-background-color-focus: sass.fn-naming-var("checkbox", "background-color", "focus");
12
+ $checkbox-background-color-unchecked: sass.fn-naming-var(
13
+ "checkbox",
14
+ "background-color",
15
+ "unchecked"
16
+ );
17
+ $checkbox-background-color-checked: sass.fn-naming-var("checkbox", "background-color", "checked");
14
18
  $checkbox-background-color-disabled: sass.fn-naming-var("checkbox", "background-color", "disabled");
15
19
 
16
20
  $checkbox-color: sass.fn-naming-var("checkbox", "color");
17
21
 
18
- $checkbox-border-color: sass.fn-naming-var("checkbox", "border-color");
19
- $checkbox-border-color-inactive: sass.fn-naming-var("checkbox", "border-color", "inactive");
22
+ $checkbox-border-color-hover: sass.fn-naming-var("checkbox", "border-color", "hover");
23
+ $checkbox-border-color-focus: sass.fn-naming-var("checkbox", "border-color", "focus");
24
+ $checkbox-border-color-checked: sass.fn-naming-var("checkbox", "border-color", "checked");
25
+ $checkbox-border-color-unchecked: sass.fn-naming-var("checkbox", "border-color", "unchecked");
26
+ $checkbox-border-color-disabled: sass.fn-naming-var("checkbox", "border-color", "disabled");
27
+
28
+ $checkbox-box-shadow-color-focus: sass.fn-naming-var("checkbox", "box-shadow-color", "focus");
20
29
 
21
30
  $transition-duration: sass.fn-naming-var("motion", "duration");
22
31
  $border-radius: sass.fn-naming-var("border-radius");
@@ -37,7 +46,7 @@ $border-radius: sass.fn-naming-var("border-radius");
37
46
  display: flex;
38
47
  align-items: center;
39
48
  justify-content: center;
40
- border: 1px solid rgb(#{$checkbox-border-color-inactive});
49
+ border: 1px solid rgb(#{$checkbox-color});
41
50
  background-color: transparent;
42
51
  transition:
43
52
  box-shadow #{$transition-duration} ease,
@@ -53,7 +62,7 @@ $border-radius: sass.fn-naming-var("border-radius");
53
62
  content: "";
54
63
  width: 0.2rem;
55
64
  height: 0.5rem;
56
- border: solid rgb(#{$checkbox-background-color-contrast});
65
+ border: solid rgb(#{$checkbox-color});
57
66
  border-width: 0 2px 2px 0;
58
67
  transform: rotate(45deg) scale(0);
59
68
  transform-origin: center;
@@ -77,13 +86,12 @@ $border-radius: sass.fn-naming-var("border-radius");
77
86
  &:not(.on) {
78
87
  .#{$checkbox-icon} {
79
88
  background-color: transparent;
80
- border-color: rgb(#{$checkbox-border-color-inactive});
89
+ border-color: rgb(#{$checkbox-border-color-unchecked});
81
90
  }
82
91
 
83
92
  &:not(.disabled):not(.readonly):hover {
84
93
  .#{$checkbox-icon} {
85
- border-color: rgb(#{$checkbox-border-color});
86
- background-color: rgba(#{$checkbox-background-color-hover}, 0.2);
94
+ border-color: rgb(#{$checkbox-border-color-hover});
87
95
  }
88
96
  }
89
97
  }
@@ -91,8 +99,8 @@ $border-radius: sass.fn-naming-var("border-radius");
91
99
  &.on {
92
100
  &.fill {
93
101
  .#{$checkbox-icon} {
94
- background-color: rgb(#{$checkbox-background-color});
95
- border-color: rgb(#{$checkbox-background-color});
102
+ background-color: rgb(#{$checkbox-background-color-checked});
103
+ border-color: rgb(#{$checkbox-border-color-checked});
96
104
 
97
105
  &::after {
98
106
  transform: rotate(45deg) scale(1);
@@ -102,11 +110,11 @@ $border-radius: sass.fn-naming-var("border-radius");
102
110
 
103
111
  &.outline {
104
112
  .#{$checkbox-icon} {
105
- border-color: rgb(#{$checkbox-background-color});
113
+ border-color: rgb(#{$checkbox-border-color-checked});
106
114
 
107
115
  &::after {
108
116
  transform: rotate(45deg) scale(1);
109
- border-color: rgb(#{$checkbox-background-color});
117
+ border-color: rgb(#{$checkbox-background-color-checked});
110
118
  }
111
119
  }
112
120
  }
@@ -119,7 +127,7 @@ $border-radius: sass.fn-naming-var("border-radius");
119
127
  &.on {
120
128
  .#{$checkbox-icon} {
121
129
  background-color: rgb(#{$checkbox-background-color-disabled});
122
- border-color: rgb(#{$checkbox-background-color-disabled});
130
+ border-color: rgb(#{$checkbox-border-color-disabled});
123
131
  }
124
132
  }
125
133
  }
@@ -131,8 +139,12 @@ $border-radius: sass.fn-naming-var("border-radius");
131
139
  &.focus {
132
140
  &.shadow {
133
141
  .#{$checkbox-icon} {
134
- box-shadow: 0 0 0 0.25rem rgb(#{$checkbox-border-color}, 0.25);
142
+ box-shadow: 0 0 0 0.25rem rgb(#{$checkbox-box-shadow-color-focus}, 0.25);
135
143
  }
136
144
  }
145
+
146
+ .#{$checkbox-icon} {
147
+ border-color: rgb(#{$checkbox-border-color-focus});
148
+ }
137
149
  }
138
150
  }
@@ -32,7 +32,7 @@ const checkboxAttrs = computed(() => {
32
32
  return {
33
33
  class: [
34
34
  withPrefix(["layer", "checkbox"]),
35
- withPrefix(["role", props.modelValue && !props.disabled ? role : baseRole]),
35
+ withPrefix(["role", role]),
36
36
  withPrefix(["shape", shape]),
37
37
  withPrefix("checkbox"),
38
38
  props.variant,
@@ -40,7 +40,7 @@ const checkboxAttrs = computed(() => {
40
40
  on: props.modelValue,
41
41
  disabled: props.disabled,
42
42
  readonly: props.readonly,
43
- shadow,
43
+ shadow: shadow && !props.disabled && isFocused.value,
44
44
  focus: isFocused.value,
45
45
  },
46
46
  ],
@@ -36,19 +36,17 @@ const shape = computed(() => {
36
36
  return props.shape || appState?.shape.value || "";
37
37
  });
38
38
 
39
- const role = computed(() => {
40
- return props.role || appState?.role.value || "";
41
- });
42
-
43
39
  const divider = computed(() => {
44
40
  return (props.divider !== undefined ? props.divider : appState?.divider.value) ?? false;
45
41
  });
46
42
 
47
43
  const decisionModalAttrs = computed(() => {
44
+ const role = props.role || appState?.role.value || "";
45
+
48
46
  return {
49
47
  class: [
50
48
  withPrefix(["layer", "decision-modal"]),
51
- withPrefix(["role", role.value]),
49
+ withPrefix(["role", role]),
52
50
  withPrefix(["shape", shape.value]),
53
51
  withPrefix("decision-modal"),
54
52
  {
@@ -0,0 +1,6 @@
1
+ <div ref="rootRef" v-bind="wrapperAttrs">
2
+ <slot name="trigger" :toggle="toggle" :isOpen="isOpen" />
3
+ <div v-show="isOpen" v-bind="panelAttrs">
4
+ <slot />
5
+ </div>
6
+ </div>
@@ -0,0 +1,74 @@
1
+ @use "@toife/sass-layer-generator" as sass;
2
+
3
+ $dropdown: sass.fn-naming-prefix("dropdown");
4
+ $dropdown-panel: sass.fn-naming-prefix("dropdown-panel");
5
+
6
+ $dropdown-border-color: sass.fn-naming-var("dropdown", "border-color");
7
+ $dropdown-background-color: sass.fn-naming-var("dropdown", "background-color");
8
+ $dropdown-color: sass.fn-naming-var("dropdown", "color");
9
+ $border-radius: sass.fn-naming-var("border-radius");
10
+ $border-width: sass.fn-naming-var("stroke", "width");
11
+ $spacing-x: sass.fn-naming-var("spacing", "x");
12
+ $spacing-y: sass.fn-naming-var("spacing", "y");
13
+ $size-font-size: sass.fn-naming-var("font-size");
14
+ $size-line-height: sass.fn-naming-var("line-height");
15
+ $transition-duration: sass.fn-naming-var("motion", "duration");
16
+
17
+ .#{$dropdown} {
18
+ position: relative;
19
+ display: block;
20
+ width: 100%;
21
+
22
+ &.disabled {
23
+ opacity: 0.5;
24
+ cursor: not-allowed;
25
+ }
26
+ }
27
+
28
+ .#{$dropdown-panel} {
29
+ position: absolute;
30
+ z-index: 20;
31
+ min-width: 100%;
32
+ box-sizing: border-box;
33
+ margin-top: calc(#{$spacing-y} * 0.25);
34
+ margin-bottom: calc(#{$spacing-y} * 0.25);
35
+ padding: calc(#{$spacing-y} * 0.5) 0;
36
+ border-radius: #{$border-radius};
37
+ border-width: #{$border-width};
38
+ border-style: solid;
39
+ border-color: rgb(#{$dropdown-border-color});
40
+ background-color: rgb(#{$dropdown-background-color});
41
+ color: rgb(#{$dropdown-color});
42
+ font-size: #{$size-font-size};
43
+ line-height: #{$size-line-height};
44
+ box-shadow: 0 #{$border-width} #{$spacing-y} rgb(#{$dropdown-border-color}, 0.12);
45
+ max-height: min(50vh, 16rem);
46
+ overflow-y: auto;
47
+ transition:
48
+ border-color #{$transition-duration} ease,
49
+ background-color #{$transition-duration} ease,
50
+ color #{$transition-duration} ease,
51
+ box-shadow #{$transition-duration} ease;
52
+
53
+ &.bottom-start {
54
+ top: 100%;
55
+ left: 0;
56
+ }
57
+
58
+ &.bottom-end {
59
+ top: 100%;
60
+ right: 0;
61
+ left: auto;
62
+ }
63
+
64
+ &.top-start {
65
+ bottom: 100%;
66
+ left: 0;
67
+ }
68
+
69
+ &.top-end {
70
+ bottom: 100%;
71
+ right: 0;
72
+ left: auto;
73
+ }
74
+ }
@@ -0,0 +1,19 @@
1
+ import type { AppDirection } from "../app";
2
+
3
+ export type DropdownPlacement = "bottom-start" | "bottom-end" | "top-start" | "top-end";
4
+
5
+ export type DropdownProps = {
6
+ modelValue?: boolean;
7
+ disabled?: boolean;
8
+ placement?: DropdownPlacement;
9
+ role?: string;
10
+ shadow?: boolean;
11
+ shape?: string;
12
+ direction?: AppDirection;
13
+ };
14
+
15
+ export type DropdownEmit = {
16
+ (e: "update:modelValue", open: boolean): void;
17
+ (e: "open"): void;
18
+ (e: "close"): void;
19
+ };
@@ -0,0 +1,109 @@
1
+ <style lang="scss" src="./dropdown.scss" scoped></style>
2
+ <template src="./dropdown.html"></template>
3
+ <script lang="ts" setup>
4
+ import { computed, inject, onMounted, onUnmounted, ref, watch } from "vue";
5
+ import type { DropdownEmit, DropdownProps } from "./dropdown.type";
6
+ import { withPrefix } from "../../utils";
7
+ import { type AppProviderState, APP_PROVIDER_STATE_KEY } from "../app";
8
+
9
+ // Component setup (props, emits, injects)
10
+ // ----------------------------------------------------------------------------
11
+ const props = withDefaults(defineProps<DropdownProps>(), {
12
+ modelValue: false,
13
+ disabled: false,
14
+ placement: "bottom-start",
15
+ role: undefined,
16
+ shadow: undefined,
17
+ shape: undefined,
18
+ direction: undefined,
19
+ });
20
+ const emit = defineEmits<DropdownEmit>();
21
+ const appState = inject<AppProviderState>(APP_PROVIDER_STATE_KEY);
22
+
23
+ // Reactive state
24
+ // ----------------------------------------------------------------------------
25
+ const rootRef = ref<HTMLElement | null>(null);
26
+ const isOpen = ref(false);
27
+
28
+ // Computed properties
29
+ // ----------------------------------------------------------------------------
30
+ const wrapperAttrs = computed(() => {
31
+ const role = props.role ?? appState?.role.value ?? "";
32
+ const shadow = props.shadow ?? appState?.shadow.value ?? false;
33
+ const shape = props.shape ?? appState?.shape.value ?? "";
34
+
35
+ return {
36
+ class: [
37
+ withPrefix(["layer", "dropdown"]),
38
+ withPrefix(["role", role]),
39
+ withPrefix(["shape", shape]),
40
+ withPrefix("dropdown"),
41
+ {
42
+ open: isOpen.value,
43
+ disabled: props.disabled,
44
+ shadow: shadow,
45
+ },
46
+ ],
47
+ };
48
+ });
49
+
50
+ const panelAttrs = computed(() => {
51
+ return {
52
+ class: [withPrefix("dropdown-panel"), props.placement],
53
+ };
54
+ });
55
+
56
+ // Methods
57
+ // ----------------------------------------------------------------------------
58
+ const toggle = () => {
59
+ if (props.disabled) return;
60
+ isOpen.value = !isOpen.value;
61
+ emit("update:modelValue", isOpen.value);
62
+ };
63
+
64
+ const close = () => {
65
+ if (!isOpen.value) return;
66
+ isOpen.value = false;
67
+ emit("update:modelValue", false);
68
+ };
69
+
70
+ const onDocPointerDown = (e: PointerEvent) => {
71
+ if (!isOpen.value) return;
72
+ const root = rootRef.value;
73
+ if (root && !root.contains(e.target as Node)) {
74
+ close();
75
+ }
76
+ };
77
+
78
+ const onDocKeydown = (e: KeyboardEvent) => {
79
+ if (e.key === "Escape" && isOpen.value) {
80
+ e.preventDefault();
81
+ close();
82
+ }
83
+ };
84
+
85
+ // Lifecycle
86
+ // ----------------------------------------------------------------------------
87
+ watch(
88
+ () => props.modelValue,
89
+ (open) => {
90
+ isOpen.value = open;
91
+ },
92
+ { immediate: true }
93
+ );
94
+
95
+ watch(isOpen, (next, prev) => {
96
+ if (next && !prev) emit("open");
97
+ if (!next && prev) emit("close");
98
+ });
99
+
100
+ onMounted(() => {
101
+ document.addEventListener("pointerdown", onDocPointerDown, true);
102
+ document.addEventListener("keydown", onDocKeydown, true);
103
+ });
104
+
105
+ onUnmounted(() => {
106
+ document.removeEventListener("pointerdown", onDocPointerDown, true);
107
+ document.removeEventListener("keydown", onDocKeydown, true);
108
+ });
109
+ </script>
@@ -0,0 +1,2 @@
1
+ export { default as Dropdown } from "./dropdown.vue";
2
+ export type { DropdownPlacement, DropdownProps, DropdownEmit } from "./dropdown.type";
@@ -1 +1,12 @@
1
- <OutlineField v-if="props.variant === 'outline'" v-bind="fieldAttrs" />
1
+ <OutlineField v-if="props.variant === 'outline'" v-bind="fieldAttrs">
2
+ <template #start-input>
3
+ <slot name="start-input"></slot>
4
+ </template>
5
+ <template #input>
6
+ <slot name="input"></slot>
7
+ </template>
8
+ <template #end-input>
9
+ <slot name="end-input"></slot>
10
+ </template>
11
+ <slot></slot>
12
+ </OutlineField>
@@ -1,30 +1,34 @@
1
1
  <div v-bind="fieldAttrs">
2
2
  <div v-bind="fieldContentAttrs">
3
- <div
4
- v-bind="fieldInputAttrs"
5
- @input="onInput"
6
- @compositionstart="onCompositionStart"
7
- @compositionend="onCompositionEnd"
8
- @focus="onFocus"
9
- @blur="onBlur"
10
- @beforeinput="onBeforeinput"
11
- ref="contentRef"
12
- v-text="content"
13
- v-if="type !== 'password'"
14
- ></div>
15
- <input
16
- v-bind="fieldInputAttrs"
17
- @input="onInput"
18
- @compositionstart="onCompositionStart"
19
- @compositionend="onCompositionEnd"
20
- @focus="onFocus"
21
- @blur="onBlur"
22
- @beforeinput="onBeforeinput"
23
- ref="contentRef"
24
- :value="content"
25
- type="password"
26
- v-else
27
- ></input>
3
+ <slot name="start-input"></slot>
4
+ <slot name="input">
5
+ <div
6
+ v-bind="fieldInputAttrs"
7
+ @input="onInput"
8
+ @compositionstart="onCompositionStart"
9
+ @compositionend="onCompositionEnd"
10
+ @focus="onFocus"
11
+ @blur="onBlur"
12
+ @beforeinput="onBeforeinput"
13
+ ref="contentRef"
14
+ v-text="content"
15
+ v-if="type !== 'password'"
16
+ ></div>
17
+ <input
18
+ v-bind="fieldInputAttrs"
19
+ @input="onInput"
20
+ @compositionstart="onCompositionStart"
21
+ @compositionend="onCompositionEnd"
22
+ @focus="onFocus"
23
+ @blur="onBlur"
24
+ @beforeinput="onBeforeinput"
25
+ ref="contentRef"
26
+ :value="content"
27
+ type="password"
28
+ v-else
29
+ />
30
+ </slot>
31
+ <slot name="end-input"></slot>
28
32
  </div>
29
33
  <div v-bind="fieldMessageAttrs" v-if="message">{{ message }}</div>
30
34
  <div v-bind="fieldHelpAttrs" v-if="help">{{ help }}</div>
@@ -6,8 +6,6 @@ $field-content: sass.fn-naming-prefix("field-content");
6
6
  $field-input: sass.fn-naming-prefix("field-input");
7
7
  $field-message: sass.fn-naming-prefix("field-message");
8
8
  $field-help: sass.fn-naming-prefix("field-help");
9
- $field-direction-left: sass.fn-naming-prefix("direction", "left");
10
- $field-direction-right: sass.fn-naming-prefix("direction", "right");
11
9
 
12
10
  // Property name - layer: field
13
11
  $field-border-color: sass.fn-naming-var("field", "border-color");
@@ -30,6 +28,9 @@ $size-line-height: sass.fn-naming-var("line-height");
30
28
 
31
29
  $transition-duration: sass.fn-naming-var("motion", "duration");
32
30
 
31
+ $direction-text-align: sass.fn-naming-var("text-align");
32
+ $direction-justify-content: sass.fn-naming-var("justify-content");
33
+
33
34
  .#{$field} {
34
35
  width: 100%;
35
36
  box-shadow: none !important;
@@ -50,6 +51,9 @@ $transition-duration: sass.fn-naming-var("motion", "duration");
50
51
  line-height: 0;
51
52
  height: fit-content;
52
53
  position: relative;
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
53
57
  }
54
58
 
55
59
  // Input
@@ -68,6 +72,9 @@ $transition-duration: sass.fn-naming-var("motion", "duration");
68
72
  font-size: #{$size-font-size};
69
73
  line-height: #{$size-line-height};
70
74
  box-sizing: border-box;
75
+ width: 100%;
76
+ overflow: auto;
77
+ flex: 1;
71
78
  transition:
72
79
  box-shadow #{$transition-duration} ease,
73
80
  border-color #{$transition-duration} ease,
@@ -164,7 +171,6 @@ $transition-duration: sass.fn-naming-var("motion", "duration");
164
171
  display: block;
165
172
  white-space: pre-wrap;
166
173
  word-break: break-word;
167
- padding: inherit;
168
174
  line-height: inherit;
169
175
  font-size: inherit;
170
176
  display: flex;
@@ -201,25 +207,12 @@ $transition-duration: sass.fn-naming-var("motion", "duration");
201
207
  }
202
208
 
203
209
  // Direction
204
- &.#{$field-direction-left} {
205
- .#{$field-input} {
206
- justify-content: start;
207
- text-align: left;
208
- }
209
-
210
- .#{$field-input}::before {
211
- left: 0;
212
- }
210
+ .#{$field-input} {
211
+ justify-content: #{$direction-justify-content};
212
+ text-align: #{$direction-text-align};
213
213
  }
214
214
 
215
- &.#{$field-direction-right} {
216
- .#{$field-input} {
217
- justify-content: end;
218
- text-align: right;
219
- }
220
-
221
- .#{$field-input}::before {
222
- right: 0;
223
- }
215
+ .#{$field-input}::before {
216
+ #{$direction-text-align}: 0;
224
217
  }
225
218
  }
@@ -1,4 +1,5 @@
1
1
  // Type definitions
2
2
  export type GestureIndicatorProps = {
3
3
  placement?: string;
4
+ role?: string;
4
5
  };
@@ -10,13 +10,15 @@ import { type AppProviderState, APP_PROVIDER_STATE_KEY } from "../app";
10
10
  // ----------------------------------------------------------------------------
11
11
  const props = withDefaults(defineProps<GestureIndicatorProps>(), {
12
12
  placement: "bottom",
13
+ role: undefined,
13
14
  });
14
15
  const appState = inject<AppProviderState>(APP_PROVIDER_STATE_KEY);
15
16
 
16
17
  // Computed properties
17
18
  // ----------------------------------------------------------------------------
18
19
  const gestureIndicatorAttrs = computed(() => {
19
- const role = appState?.role.value || "";
20
+ const role = props.role || appState?.role.value || "";
21
+
20
22
  return {
21
23
  class: [
22
24
  withPrefix(["layer", "gesture-indicator"]),
@@ -25,3 +25,5 @@ export * from "./route";
25
25
  export * from "./page";
26
26
  export * from "./collapse";
27
27
  export * from "./form-group";
28
+ export * from "./dropdown";
29
+ export * from "./select";
@@ -4,20 +4,22 @@
4
4
  $radio: sass.fn-naming-prefix("radio");
5
5
  $radio-icon: sass.fn-naming-prefix("radio-icon");
6
6
 
7
- // Property name - layer: radio (states align with button + checkbox: inactive + contrast)
8
- $radio-background-color: sass.fn-naming-var("radio", "background-color");
7
+ // Properties - layer: item
9
8
  $radio-background-color-hover: sass.fn-naming-var("radio", "background-color", "hover");
10
9
  $radio-background-color-focus: sass.fn-naming-var("radio", "background-color", "focus");
11
- $radio-background-color-active: sass.fn-naming-var("radio", "background-color", "active");
10
+ $radio-background-color-unchecked: sass.fn-naming-var("radio", "background-color", "unchecked");
11
+ $radio-background-color-checked: sass.fn-naming-var("radio", "background-color", "checked");
12
12
  $radio-background-color-disabled: sass.fn-naming-var("radio", "background-color", "disabled");
13
- $radio-background-color-contrast: sass.fn-naming-var("radio", "background-color", "contrast");
14
13
 
15
- $radio-border-color: sass.fn-naming-var("radio", "border-color");
14
+ $radio-color: sass.fn-naming-var("radio", "color");
15
+
16
16
  $radio-border-color-hover: sass.fn-naming-var("radio", "border-color", "hover");
17
17
  $radio-border-color-focus: sass.fn-naming-var("radio", "border-color", "focus");
18
- $radio-border-color-active: sass.fn-naming-var("radio", "border-color", "active");
18
+ $radio-border-color-checked: sass.fn-naming-var("radio", "border-color", "checked");
19
+ $radio-border-color-unchecked: sass.fn-naming-var("radio", "border-color", "unchecked");
19
20
  $radio-border-color-disabled: sass.fn-naming-var("radio", "border-color", "disabled");
20
- $radio-border-color-inactive: sass.fn-naming-var("radio", "border-color", "inactive");
21
+
22
+ $radio-box-shadow-color-focus: sass.fn-naming-var("radio", "box-shadow-color", "focus");
21
23
 
22
24
  $transition-duration: sass.fn-naming-var("motion", "duration");
23
25
  $border-radius: sass.fn-naming-var("border-radius");
@@ -38,7 +40,7 @@ $border-radius: sass.fn-naming-var("border-radius");
38
40
  display: flex;
39
41
  align-items: center;
40
42
  justify-content: center;
41
- border: 1px solid rgb(#{$radio-border-color-inactive});
43
+ border: 1px solid rgb(#{$radio-color});
42
44
  background-color: transparent;
43
45
  transition:
44
46
  box-shadow #{$transition-duration} ease,
@@ -55,8 +57,9 @@ $border-radius: sass.fn-naming-var("border-radius");
55
57
  width: 0.5rem;
56
58
  height: 0.5rem;
57
59
  border-radius: 50%;
58
- background-color: rgb(#{$radio-background-color-contrast});
60
+ background-color: rgb(#{$radio-color});
59
61
  transform: scale(0);
62
+ transform-origin: center;
60
63
  transition:
61
64
  transform #{$transition-duration} ease,
62
65
  background-color #{$transition-duration} ease,
@@ -68,13 +71,12 @@ $border-radius: sass.fn-naming-var("border-radius");
68
71
  &:not(.on) {
69
72
  .#{$radio-icon} {
70
73
  background-color: transparent;
71
- border-color: rgb(#{$radio-border-color-inactive});
74
+ border-color: rgb(#{$radio-border-color-unchecked});
72
75
  }
73
76
 
74
77
  &:not(.disabled):not(.readonly):hover {
75
78
  .#{$radio-icon} {
76
79
  border-color: rgb(#{$radio-border-color-hover});
77
- background-color: rgba(#{$radio-background-color-hover}, 0.2);
78
80
  }
79
81
  }
80
82
  }
@@ -82,8 +84,8 @@ $border-radius: sass.fn-naming-var("border-radius");
82
84
  &.on {
83
85
  &.fill {
84
86
  .#{$radio-icon} {
85
- background-color: rgb(#{$radio-background-color});
86
- border-color: rgb(#{$radio-background-color});
87
+ background-color: rgb(#{$radio-background-color-checked});
88
+ border-color: rgb(#{$radio-border-color-checked});
87
89
 
88
90
  &::after {
89
91
  transform: scale(1);
@@ -93,12 +95,11 @@ $border-radius: sass.fn-naming-var("border-radius");
93
95
 
94
96
  &.outline {
95
97
  .#{$radio-icon} {
96
- background-color: transparent;
97
- border-color: rgb(#{$radio-background-color});
98
+ border-color: rgb(#{$radio-border-color-checked});
98
99
 
99
100
  &::after {
100
- transform: scale(1.5);
101
- background-color: rgb(#{$radio-background-color});
101
+ transform: scale(1);
102
+ background-color: rgb(#{$radio-background-color-checked});
102
103
  }
103
104
  }
104
105
  }
@@ -123,8 +124,12 @@ $border-radius: sass.fn-naming-var("border-radius");
123
124
  &.focus {
124
125
  &.shadow {
125
126
  .#{$radio-icon} {
126
- box-shadow: 0 0 0 0.25rem rgb(#{$radio-border-color-focus}, 0.25);
127
+ box-shadow: 0 0 0 0.25rem rgb(#{$radio-box-shadow-color-focus}, 0.25);
127
128
  }
128
129
  }
130
+
131
+ .#{$radio-icon} {
132
+ border-color: rgb(#{$radio-border-color-focus});
133
+ }
129
134
  }
130
135
  }
@@ -25,14 +25,6 @@ const isFocused = ref(false);
25
25
 
26
26
  // Computed properties
27
27
  // ----------------------------------------------------------------------------
28
- const role = computed(() => {
29
- return props.role || radioGroupState?.role.value || appState?.role.value || "";
30
- });
31
-
32
- const variant = computed(() => {
33
- return props.variant || radioGroupState?.variant.value || "";
34
- });
35
-
36
28
  const disabled = computed(() => {
37
29
  return props.disabled || (radioGroupState?.disabled.value ?? false);
38
30
  });
@@ -41,29 +33,23 @@ const readonly = computed(() => {
41
33
  return props.readonly || (radioGroupState?.readonly.value ?? false);
42
34
  });
43
35
 
44
- const isChecked = computed(() => {
45
- return radioGroupState?.modelValue.value === props.value;
46
- });
47
-
48
36
  const radioAttrs = computed(() => {
49
- const shadow =
50
- (props?.shadow !== undefined ? props.shadow : radioGroupState?.shadow.value) ?? false;
37
+ const shadow = (props?.shadow !== undefined ? props.shadow : appState?.shadow.value) ?? false;
38
+ const role = props.role || radioGroupState?.role.value || appState?.role.value || "";
39
+ const variant = props.variant || radioGroupState?.variant.value || "";
40
+ const isChecked = radioGroupState?.modelValue.value === props.value;
41
+
51
42
  return {
52
43
  class: [
53
44
  withPrefix(["layer", "radio"]),
54
- withPrefix([
55
- "role",
56
- (isChecked.value && !disabled.value
57
- ? role.value || radioGroupState?.role.value
58
- : appState?.role.value) ?? "",
59
- ]),
45
+ withPrefix(["role", role]),
60
46
  withPrefix("radio"),
61
- variant.value,
47
+ variant,
62
48
  {
63
- on: isChecked.value,
49
+ on: isChecked,
64
50
  disabled: disabled.value,
65
51
  readonly: readonly.value,
66
- shadow,
52
+ shadow: shadow && !props.disabled && isFocused.value,
67
53
  focus: isFocused.value,
68
54
  },
69
55
  ],
@@ -0,0 +1,2 @@
1
+ export { default as Select } from "./select.vue";
2
+ export type { SelectVariant, SelectSize, SelectProps } from "./select.type";
@@ -0,0 +1,26 @@
1
+ <div v-bind="selectAttrs">
2
+ <Dropdown v-bind="dropdownAttrs">
3
+ <template #trigger="{ toggle, isOpen }">
4
+ <Field
5
+ :role="role"
6
+ variant="outline"
7
+ @click="toggle"
8
+ v-bind="fieldAttrs"
9
+ :class="{focus: isOpen}"
10
+ >
11
+ <template #end-input>
12
+ <span v-bind="selectIconAttrs" :class="{open: isOpen}"></span>
13
+ </template>
14
+ </Field>
15
+ </template>
16
+ <button
17
+ v-for="item in menuItems"
18
+ :key="item"
19
+ type="button"
20
+ class="dropdown-demo-row"
21
+ @click="pickMenu(item)"
22
+ >
23
+ {{ item }}
24
+ </button>
25
+ </Dropdown>
26
+ </div>
@@ -0,0 +1,58 @@
1
+ @use "@toife/sass-layer-generator" as sass;
2
+
3
+ // Class name
4
+ $select: sass.fn-naming-prefix("select");
5
+ $select-icon: sass.fn-naming-prefix("select-icon");
6
+ $field: sass.fn-naming-prefix("field");
7
+ $field-input: sass.fn-naming-prefix("field-input");
8
+
9
+ // Property name
10
+ $app-color: sass.fn-naming-var("app", "color");
11
+ $spacing-x: sass.fn-naming-var("spacing", "x");
12
+ $size-coefficient-x: sass.fn-naming-var("coefficient-x");
13
+ $transition-duration: sass.fn-naming-var("motion", "duration");
14
+
15
+ .#{$select} {
16
+ &.disabled {
17
+ cursor: not-allowed;
18
+ }
19
+
20
+ &.readonly {
21
+ pointer-events: none;
22
+ }
23
+
24
+ .#{$select-icon} {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ flex-shrink: 0;
29
+ margin-inline-end: calc(#{$spacing-x} * #{$size-coefficient-x} * 0.5);
30
+ color: inherit;
31
+ pointer-events: none;
32
+ width: 0.6rem;
33
+ height: 0.6rem;
34
+ border-radius: 0.1rem;
35
+ border-right: 2px solid rgb(var(--t-app-color));
36
+ border-bottom: 2px solid rgb(var(--t-app-color));
37
+ margin: calc(var(--t-spacing-y) * var(--t-coefficient-y))
38
+ calc(var(--t-spacing-x) * var(--t-coefficient-x));
39
+ transition: transform #{$transition-duration} ease;
40
+ transform-origin: center;
41
+
42
+ &.open {
43
+ transform: rotate(225deg);
44
+ }
45
+
46
+ &:not(.open) {
47
+ transform: rotate(45deg);
48
+ }
49
+ }
50
+
51
+ // :deep(.#{$field}.focus .#{$select-icon} svg) {
52
+ // transform: rotate(180deg);
53
+ // }
54
+
55
+ // :deep(.#{$field-input}) {
56
+ // cursor: pointer;
57
+ // }
58
+ }
@@ -0,0 +1,32 @@
1
+ import type { FieldSize, FieldVariant } from "../field";
2
+ import type { AppDirection } from "../app";
3
+
4
+ export type SelectVariant = FieldVariant;
5
+ export type SelectSize = FieldSize;
6
+
7
+ export type SelectProps = {
8
+ // Wrapper
9
+ modelValue?: string;
10
+ name?: string;
11
+ variant?: FieldVariant;
12
+ role?: string;
13
+ shape?: string;
14
+ size?: FieldSize;
15
+ shadow?: boolean;
16
+ direction?: AppDirection;
17
+
18
+ // Input
19
+ id?: string;
20
+ value?: string;
21
+ placeholder?: string;
22
+ disabled?: boolean;
23
+ readonly?: boolean;
24
+ autocomplete?: string;
25
+ tabindex?: number | string;
26
+ line?: number | string;
27
+ maxLine?: number | string;
28
+
29
+ // Support
30
+ message?: string;
31
+ help?: string;
32
+ };
@@ -0,0 +1,89 @@
1
+ <style lang="scss" src="./select.scss" scoped></style>
2
+ <template src="./select.html"></template>
3
+ <script lang="ts" setup>
4
+ import { computed, inject } from "vue";
5
+ import { type SelectProps } from "./select.type";
6
+ import { property, withPrefix } from "../../utils";
7
+ import { type AppProviderState, APP_PROVIDER_STATE_KEY } from "../app";
8
+ import { Field } from "../field";
9
+ import { Dropdown } from "../dropdown";
10
+
11
+ // Component setup (props, emits, injects)
12
+ // ----------------------------------------------------------------------------
13
+ const props = withDefaults(defineProps<SelectProps>(), {
14
+ modelValue: "",
15
+ type: "text",
16
+ size: "standard",
17
+ disabled: false,
18
+ readonly: false,
19
+ message: "",
20
+ help: "",
21
+ variant: "outline",
22
+ placeholder: "",
23
+ shadow: undefined,
24
+ direction: undefined,
25
+ });
26
+ const appState = inject<AppProviderState>(APP_PROVIDER_STATE_KEY);
27
+
28
+ // Computed properties
29
+ // ----------------------------------------------------------------------------
30
+ const role = computed(() => {
31
+ return props.role || appState?.role.value || "";
32
+ });
33
+
34
+ const direction = computed(() => {
35
+ return props.direction || appState?.direction?.value || "left";
36
+ });
37
+
38
+ const shadow = computed(() => {
39
+ return props.shadow || appState?.shadow.value || false;
40
+ });
41
+
42
+ const shape = computed(() => {
43
+ return props.shape || appState?.shape.value || "";
44
+ });
45
+
46
+ const selectAttrs = computed(() => {
47
+ return {
48
+ class: [
49
+ withPrefix(["layer", "select"]),
50
+ withPrefix("select"),
51
+ {
52
+ disabled: props.disabled,
53
+ readonly: props.readonly,
54
+ },
55
+ ],
56
+ };
57
+ });
58
+
59
+ const dropdownAttrs = computed(() => {
60
+ return {
61
+ role: role.value,
62
+ direction: direction.value,
63
+ shadow: shadow.value,
64
+ shape: shape.value,
65
+ disabled: props.disabled,
66
+ readonly: props.readonly,
67
+ };
68
+ });
69
+
70
+ const fieldAttrs = computed(() => {
71
+ return {
72
+ modelValue: props.modelValue,
73
+ size: props.size,
74
+ message: props.message,
75
+ help: props.help,
76
+ variant: props.variant,
77
+ placeholder: props.placeholder,
78
+ direction: direction.value,
79
+ role: role.value,
80
+ shape: shape.value,
81
+ readonly: true,
82
+ shadow: shadow.value,
83
+ };
84
+ });
85
+
86
+ const selectIconAttrs = computed(() => ({
87
+ class: [withPrefix("select-icon")],
88
+ }));
89
+ </script>
@@ -8,13 +8,15 @@ $shape-pill: sass.fn-naming-prefix("shape-pill");
8
8
  $switch-wrapper: sass.fn-naming-prefix("switch-wrapper");
9
9
 
10
10
  // Property name - layer: switch (track + thumb; states like button + inactive)
11
- $switch-background-color: sass.fn-naming-var("switch", "background-color");
12
- $switch-background-color-hover: sass.fn-naming-var("switch", "background-color", "hover");
13
- $switch-background-color-inactive: sass.fn-naming-var("switch", "background-color", "inactive");
11
+ $switch-background-color-off: sass.fn-naming-var("switch", "background-color", "off");
12
+ $switch-background-color-on: sass.fn-naming-var("switch", "background-color", "on");
14
13
  $switch-background-color-disabled: sass.fn-naming-var("switch", "background-color", "disabled");
15
- $switch-background-color-contrast: sass.fn-naming-var("switch", "background-color", "contrast");
16
14
 
17
- $switch-border-color-focus: sass.fn-naming-var("switch", "border-color", "focus");
15
+ $switch-color-on: sass.fn-naming-var("switch", "color", "on");
16
+ $switch-color-off: sass.fn-naming-var("switch", "color", "off");
17
+ $switch-color-disabled: sass.fn-naming-var("switch", "color", "disabled");
18
+
19
+ $switch-box-shadow-color-focus: sass.fn-naming-var("switch", "box-shadow-color", "focus");
18
20
 
19
21
  $transition-duration: sass.fn-naming-var("motion", "duration");
20
22
  $border-radius: sass.fn-naming-var("border-radius");
@@ -31,7 +33,6 @@ $spacing-x: sass.fn-naming-var("spacing", "x");
31
33
  height: 1.5rem;
32
34
  aspect-ratio: 11/6;
33
35
  position: relative;
34
- overflow: hidden;
35
36
  transition:
36
37
  background-color #{$transition-duration} ease,
37
38
  color #{$transition-duration} ease,
@@ -44,7 +45,6 @@ $spacing-x: sass.fn-naming-var("spacing", "x");
44
45
  height: calc(100% - 0.3rem);
45
46
  position: absolute;
46
47
  top: 0;
47
- background-color: rgb(#{$switch-background-color-contrast});
48
48
  border-radius: #{$border-radius};
49
49
  aspect-ratio: 1/1;
50
50
  margin: 0.15rem;
@@ -76,6 +76,10 @@ $spacing-x: sass.fn-naming-var("spacing", "x");
76
76
  background-color: rgb(#{$switch-background-color-disabled});
77
77
  cursor: not-allowed;
78
78
  opacity: 0.3;
79
+
80
+ .#{$switch-icon} {
81
+ background-color: rgb(#{$switch-color-disabled});
82
+ }
79
83
  }
80
84
  }
81
85
 
@@ -89,26 +93,24 @@ $spacing-x: sass.fn-naming-var("spacing", "x");
89
93
 
90
94
  &:not(.on) {
91
95
  .#{$switch} {
92
- background-color: rgb(#{$switch-background-color-inactive});
96
+ background-color: rgb(#{$switch-background-color-off});
93
97
 
94
98
  .#{$switch-icon} {
99
+ background-color: rgb(#{$switch-color-off});
95
100
  left: 0;
96
101
  }
97
-
98
- &:not(.disabled):not(.readonly):hover {
99
- background-color: rgb(#{$switch-background-color-hover});
100
- }
101
102
  }
102
103
  }
103
104
 
104
105
  &.on {
105
106
  .#{$switch} {
106
- background-color: rgb(#{$switch-background-color});
107
+ background-color: rgb(#{$switch-background-color-on});
107
108
 
108
109
  .#{$switch-icon} {
109
110
  right: 0;
110
111
  animation: shrink #{$transition-duration} linear;
111
112
  animation-fill-mode: forwards;
113
+ background-color: rgb(#{$switch-color-on});
112
114
  }
113
115
  }
114
116
  }
@@ -116,7 +118,7 @@ $spacing-x: sass.fn-naming-var("spacing", "x");
116
118
  &.focus {
117
119
  &.shadow {
118
120
  .#{$switch} {
119
- box-shadow: 0 0 0 0.25rem rgb(#{$switch-border-color-focus}, 0.25);
121
+ box-shadow: 0 0 0 0.25rem rgb(#{$switch-box-shadow-color-focus}, 0.25);
120
122
  }
121
123
  }
122
124
  }
@@ -30,7 +30,7 @@ const switchWrapperAttrs = computed(() => {
30
30
  {
31
31
  disabled: props.disabled,
32
32
  readonly: props.readonly,
33
- shadow,
33
+ shadow: shadow && !props.disabled && isFocused.value,
34
34
  focus: isFocused.value,
35
35
  on: props.modelValue,
36
36
  },
@@ -45,7 +45,7 @@ const switchAttrs = computed(() => {
45
45
  return {
46
46
  class: [
47
47
  withPrefix(["layer", "switch"]),
48
- withPrefix(["role", props.modelValue && !props.disabled ? role : baseRole]),
48
+ withPrefix(["role", role]),
49
49
  withPrefix(["shape", shape]),
50
50
  withPrefix("switch"),
51
51
  ],
package/src/factory.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  Container,
14
14
  DecisionModal,
15
15
  Divider,
16
+ Dropdown,
16
17
  Field,
17
18
  FormGroup,
18
19
  GestureIndicator,
@@ -28,6 +29,7 @@ import {
28
29
  RouteProvider,
29
30
  RouteOutlet,
30
31
  SegmentedField,
32
+ Select,
31
33
  Skeleton,
32
34
  Switch,
33
35
  Tab,
@@ -59,6 +61,7 @@ export const createToife = (options?: CreateToifeOptions) => {
59
61
  app.component(prefix + "container", Container);
60
62
  app.component(prefix + "decision-modal", DecisionModal);
61
63
  app.component(prefix + "divider", Divider);
64
+ app.component(prefix + "dropdown", Dropdown);
62
65
  app.component(prefix + "field", Field);
63
66
  app.component(prefix + "form-group", FormGroup);
64
67
  app.component(prefix + "gesture-indicator", GestureIndicator);
@@ -72,6 +75,7 @@ export const createToife = (options?: CreateToifeOptions) => {
72
75
  app.component(prefix + "route-provider", RouteProvider);
73
76
  app.component(prefix + "route-outlet", RouteOutlet);
74
77
  app.component(prefix + "segmented-field", SegmentedField);
78
+ app.component(prefix + "select", Select);
75
79
  app.component(prefix + "skeleton", Skeleton);
76
80
  app.component(prefix + "switch", Switch);
77
81
  app.component(prefix + "tab", Tab);