diff --git a/frontend/src/components/SwitchGroup.vue b/frontend/src/components/SwitchGroup.vue new file mode 100644 index 0000000..09fab88 --- /dev/null +++ b/frontend/src/components/SwitchGroup.vue @@ -0,0 +1,60 @@ + + + diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css index 1151872..a44ab42 100644 --- a/frontend/src/styles/main.css +++ b/frontend/src/styles/main.css @@ -300,7 +300,7 @@ svg { .plain-button, .row-actions button, .inline-row button, -.appearance-row button { +.appearance-row__delete { --btn-bg: var(--surface); --btn-fg: var(--ink); --btn-border: var(--line-strong); @@ -333,7 +333,7 @@ svg { .plain-button:hover, .row-actions button:hover, .inline-row button:hover, -.appearance-row button:hover { +.appearance-row__delete:hover { transform: translateY(-2px); box-shadow: 0 5px 0 var(--line-strong); } @@ -344,7 +344,7 @@ svg { .plain-button:active, .row-actions button:active, .inline-row button:active, -.appearance-row button:active { +.appearance-row__delete:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--line-strong); } @@ -370,7 +370,7 @@ svg { .plain-button, .row-actions button, .inline-row button, -.appearance-row button { +.appearance-row__delete { --btn-bg: var(--surface); --btn-border: var(--line); box-shadow: none; @@ -1165,7 +1165,7 @@ button:disabled, .row-actions button, .inline-row button, -.appearance-row button { +.appearance-row__delete { min-height: 34px; padding: 6px 10px; font-size: 14px; @@ -1225,6 +1225,7 @@ button:disabled, .appearance-row { display: grid; grid-template-columns: 1fr; + gap: 12px; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-card); @@ -1235,6 +1236,134 @@ button:disabled, min-width: 64px; } +.appearance-row__main { + display: grid; + grid-template-columns: minmax(260px, 1.2fr) minmax(240px, 1fr) minmax(180px, 0.9fr) 82px max-content; + gap: 12px; + align-items: start; +} + +.appearance-row__pokemon, +.appearance-row__maps, +.appearance-row__rarity, +.appearance-row__main .switch-group { + min-width: 0; + width: 100%; +} + +.appearance-row__rarity input { + width: 100%; +} + +.appearance-row__delete { + align-self: end; + justify-self: end; + min-height: 32px; + padding: 5px 9px; + font-size: 13px; +} + +.appearance-row .tags-select, +.appearance-row .tags-select__trigger { + width: 100%; +} + +.switch-group { + min-width: 0; + min-inline-size: 0; + display: grid; + gap: 7px; + margin: 0; + padding: 0; + border: 0; +} + +.switch-group legend { + padding: 0; + color: var(--ink-soft); + font-size: 14px; + font-weight: 850; +} + +.switch-group__options { + display: flex; + flex-wrap: wrap; + gap: 8px 12px; + align-items: center; +} + +.switch-control { + position: relative; + display: inline-flex; + align-items: center; + gap: 9px; + min-height: 44px; + color: var(--ink-soft); + font-weight: 850; + cursor: pointer; + user-select: none; +} + +.switch-control--stacked { + min-width: 62px; + align-items: center; + flex-direction: column; + gap: 6px; +} + +.switch-control__label { + color: var(--ink-soft); + font-size: 13px; + line-height: 1.2; + text-align: center; + overflow-wrap: anywhere; +} + +.switch-control input { + position: absolute; + inline-size: 1px; + block-size: 1px; + min-width: 0; + margin: 0; + opacity: 0; +} + +.switch-track { + position: relative; + width: 48px; + height: 28px; + flex: 0 0 auto; + border: 2px solid var(--line-strong); + border-radius: 999px; + background: var(--line); + transition: background 0.16s ease; +} + +.switch-track::after { + content: ""; + position: absolute; + top: 2px; + left: 2px; + width: 20px; + height: 20px; + border-radius: 50%; + background: var(--surface); + box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); + transition: transform 0.16s ease; +} + +.switch-control input:focus-visible + .switch-track { + box-shadow: 0 0 0 4px rgba(42, 117, 187, 0.16); +} + +.switch-control input:checked + .switch-track { + background: var(--pokemon-blue); +} + +.switch-control input:checked + .switch-track::after { + transform: translateX(20px); +} + @media (max-width: 900px) { .top-nav { grid-template-columns: 1fr; @@ -1271,6 +1400,10 @@ button:disabled, .admin-layout { grid-template-columns: 1fr; } + + .appearance-row__main { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } } @media (max-width: 640px) { @@ -1322,4 +1455,8 @@ button:disabled, .inline-row .tags-select { width: 100%; } + + .appearance-row__main { + grid-template-columns: 1fr; + } } diff --git a/frontend/src/views/HabitatEdit.vue b/frontend/src/views/HabitatEdit.vue index 2ba27ff..c6ac0e8 100644 --- a/frontend/src/views/HabitatEdit.vue +++ b/frontend/src/views/HabitatEdit.vue @@ -4,6 +4,7 @@ import { useRoute, useRouter } from 'vue-router'; import PageHeader from '../components/PageHeader.vue'; import Skeleton from '../components/Skeleton.vue'; import StatusMessage from '../components/StatusMessage.vue'; +import SwitchGroup from '../components/SwitchGroup.vue'; import TagsSelect from '../components/TagsSelect.vue'; import { api, @@ -40,8 +41,8 @@ const habitatForm = ref({ const timeOfDays = ['早晨', '中午', '傍晚', '晚上']; const weathers = ['晴天', '阴天', '雨天']; -const timeOfDayOptions = timeOfDays.map((name) => ({ id: name, name })); -const weatherOptions = weathers.map((name) => ({ id: name, name })); +const timeOfDayOptions = timeOfDays.map((value) => ({ value, label: value })); +const weatherOptions = weathers.map((value) => ({ value, label: value })); const routeId = computed(() => (typeof route.params.id === 'string' ? route.params.id : '')); const isEditing = computed(() => routeId.value !== ''); const itemSelectOptions = computed(() => itemRows.value.map((item) => ({ id: item.id, name: item.name }))); @@ -220,27 +221,41 @@ onMounted(() => {
- - - - - - +
+
+ + +
+ + + +
+ + +
+ + +
+ +
+ + +