@@ -8,15 +8,27 @@ SPDX-License-Identifier: AGPL-3.0-only
88 <div :class =" $style.label" >
99 <slot name =" label" ></slot >
1010 </div >
11+
1112 <div :class =" $style.body" >
12- <MkRadio
13+ <div
1314 v-for =" option in options"
1415 :key =" getKey(option.value)"
15- v-model =" model"
16- :disabled =" option.disabled"
17- :value =" option.value"
16+ v-adaptive-border
17+ :class =" [$style.optionRoot, { [$style.disabled]: option.disabled, [$style.checked]: model === option.value }]"
18+ :aria-checked =" model === option.value"
19+ :aria-disabled =" option.disabled"
20+ role =" checkbox"
21+ @click =" toggle(option)"
1822 >
19- <div :class =" [$style.optionContent, { [$style.checked]: model === option.value }]" >
23+ <input
24+ type =" radio"
25+ :disabled =" option.disabled"
26+ :class =" $style.optionInput"
27+ >
28+ <span :class =" $style.optionButton" >
29+ <span ></span >
30+ </span >
31+ <div :class =" $style.optionContent" >
2032 <i v-if =" option.icon" :class =" [$style.optionIcon, option.icon]" :style =" option.iconStyle" ></i >
2133 <div >
2234 <slot v-if =" option.slotId != null" :name =" `option-${option.slotId as SlotNames}`" ></slot >
@@ -26,8 +38,9 @@ SPDX-License-Identifier: AGPL-3.0-only
2638 </template >
2739 </div >
2840 </div >
29- </MkRadio >
41+ </div >
3042 </div >
43+
3144 <div :class =" $style.caption" >
3245 <slot name =" caption" ></slot >
3346 </div >
@@ -51,8 +64,6 @@ export type MkRadiosOption<T = OptionValue, S = string> = {
5164 </script >
5265
5366<script setup lang="ts" generic =" const T extends MkRadiosOption " >
54- import MkRadio from ' ./MkRadio.vue' ;
55-
5667defineProps <{
5768 options: T [];
5869 vertical? : boolean ;
@@ -61,18 +72,23 @@ defineProps<{
6172type SlotNames = NonNullable <T extends MkRadiosOption <any , infer U > ? U : never >;
6273
6374defineSlots <{
64- label? : () => any ;
65- caption? : () => any ;
75+ label? : () => void ;
76+ caption? : () => void ;
6677} & {
67- [K in ` option-${SlotNames } ` ]: () => any ;
78+ [K in ` option-${SlotNames } ` ]: () => void ;
6879}>();
6980
7081const model = defineModel <T [' value' ]>({ required: true });
7182
7283function getKey(value : OptionValue ): PropertyKey {
73- if (value === null ) return ' null ' ;
84+ if (value === null ) return ' ___null___ ' ;
7485 return value ;
7586}
87+
88+ function toggle(o : MkRadiosOption ): void {
89+ if (o .disabled ) return ;
90+ model .value = o .value ;
91+ }
7692 </script >
7793
7894<style lang="scss" module>
@@ -102,29 +118,110 @@ function getKey(value: OptionValue): PropertyKey {
102118 }
103119}
104120
121+ .vertical > .body {
122+ flex-direction : column ;
123+ }
124+
125+ .optionRoot {
126+ position : relative ;
127+ display : inline-flex ;
128+ align-items : center ;
129+ text-align : left ;
130+ cursor : pointer ;
131+ padding : 8px 10px ;
132+ min-width : 60px ;
133+ background-color : var (--MI_THEME-panel );
134+ background-clip : padding-box !important ;
135+ border : solid 1px var (--MI_THEME-panel );
136+ border-radius : 6px ;
137+ font-size : 90% ;
138+ transition : all 0.2s ;
139+ user-select : none ;
140+
141+ & .disabled {
142+ opacity : 0.6 ;
143+ cursor : not-allowed !important ;
144+ }
145+
146+ & :hover {
147+ border-color : var (--MI_THEME-inputBorderHover ) !important ;
148+ }
149+
150+ & :focus-within {
151+ outline : none ;
152+ box-shadow : 0 0 0 2px var (--MI_THEME-focus );
153+ }
154+
155+ & .checked {
156+ background-color : var (--MI_THEME-accentedBg ) !important ;
157+ border-color : var (--MI_THEME-accentedBg ) !important ;
158+ color : var (--MI_THEME-accent );
159+ cursor : default !important ;
160+
161+ .optionButton {
162+ border-color : var (--MI_THEME-accent );
163+
164+ & ::after {
165+ background-color : var (--MI_THEME-accent );
166+ transform : scale (1 );
167+ opacity : 1 ;
168+ }
169+ }
170+
171+ .optionCaption {
172+ color : color (from var (--MI_THEME-accent ) srgb r g b / 0.75 );
173+ }
174+ }
175+ }
176+
177+ .optionInput {
178+ position : absolute ;
179+ width : 0 ;
180+ height : 0 ;
181+ opacity : 0 ;
182+ margin : 0 ;
183+ }
184+
185+ .optionButton {
186+ position : relative ;
187+ display : inline-block ;
188+ width : 14px ;
189+ height : 14px ;
190+ background : none ;
191+ border : solid 2px var (--MI_THEME-inputBorder );
192+ border-radius : 100% ;
193+ transition : inherit ;
194+
195+ & ::after {
196+ content : ' ' ;
197+ display : block ;
198+ position : absolute ;
199+ top : 3px ;
200+ right : 3px ;
201+ bottom : 3px ;
202+ left : 3px ;
203+ border-radius : 100% ;
204+ opacity : 0 ;
205+ transform : scale (0 );
206+ transition : 0.4s cubic-bezier (0.25 , 0.8 , 0.25 , 1 );
207+ }
208+ }
209+
105210.optionContent {
106211 display : flex ;
107212 align-items : center ;
108213 gap : 6px ;
214+ margin-left : 8px ;
109215}
110216
111217.optionCaption {
112218 font-size : 0.85em ;
113219 padding : 2px 0 0 0 ;
114220 color : color (from var (--MI_THEME-fg ) srgb r g b / 0.75 );
115- }
116-
117- .optionContent.checked {
118- .optionCaption {
119- color : color (from var (--MI_THEME-accent ) srgb r g b / 0.75 );
120- }
221+ transition : all 0.2s ;
121222}
122223
123224.optionIcon {
124225 flex-shrink : 0 ;
125226}
126-
127- .vertical > .body {
128- flex-direction : column ;
129- }
130227 </style >
0 commit comments