Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/components/CheckBox/CheckBox.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ const meta: Meta<typeof CheckBox> = {
},
template: `
<div>
<CheckBox v-model="args.modelValue" :disabled="args.disabled" class="additional" />
<p>{{ args.modelValue ? 'on' : 'off' }}</p>
<CheckBox v-model="args.modelValue" v-model:indeterminate="args.indeterminate" :disabled="args.disabled" class="additional" />
<p>modelValue: {{ args.modelValue ? 'on' : 'off' }}</p>
<p>indeterminate: {{ args.indeterminate }}</p>
</div>
`,
}),
Expand All @@ -27,6 +28,7 @@ export const Default: Story = {
args: {
modelValue: true,
disabled: false,
indeterminate: false,
},
}

Expand All @@ -35,5 +37,15 @@ export const Disabled: Story = {
args: {
modelValue: true,
disabled: true,
indeterminate: false,
},
}

export const Indeterminate: Story = {
name: 'indeterminate',
args: {
modelValue: false,
disabled: false,
indeterminate: true,
},
}
75 changes: 64 additions & 11 deletions src/components/CheckBox/CheckBox.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
<template>
<label class="check-box" :class="{ 'is-disabled': disabled }">
<input
ref="inputRef"
v-bind="$attrs"
:checked="modelValue"
:disabled="disabled"
@change="onInput"
class="input"
type="checkbox"
/>
<div class="icon-wrap">
<Icon name="check" />
<div
class="icon-wrap"
:class="{
'is-checked': modelValue && !indeterminate,
'is-indeterminate': indeterminate,
}"
>
<Icon :name="iconName" />
</div>
</label>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import Icon from '@/components/Icon/Icon.vue'

export default defineComponent({
Expand All @@ -32,20 +39,52 @@ export default defineComponent({
type: Boolean,
default: false,
},
indeterminate: {
type: Boolean,
default: false,
},
},
emits: {
'update:modelValue': null,
'update:indeterminate': null,
},
setup(_, context) {
setup(props, context) {
const inputRef = ref<HTMLInputElement>()

const emitInput = (value: boolean) =>
context.emit('update:modelValue', value)

const onInput = (e: Event) => {
emitInput((e.target as HTMLInputElement).checked)
const target = e.target as HTMLInputElement

// indeterminate状態からクリックされた場合は、indeterminateをfalseにして、必ずチェック状態にする
if (props.indeterminate) {
context.emit('update:indeterminate', false)
if (inputRef.value) {
inputRef.value.checked = true
}
}

emitInput(target.checked)
}

const iconName = computed(() => (props.indeterminate ? 'minus' : 'check'))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits

ネイティブのinputtのindeterminateの属性も更新してあげると、より良いかもと思いました!

watch(
  () => props.indeterminate,
  (value) => {
    if (inputRef.value) inputRef.value.indeterminate = value
  },
  { immediate: true }
)

https://developer.mozilla.org/ja/docs/Web/API/HTMLInputElement/indeterminate

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

提案して頂いたコードは props.indeterminateの初期値がtrueだった場合、DOMへの反映がされない可能性がありましたので、watchの immediate: trueオプションを消してonMountedを追加しました。

https://github.com/lapras-inc/lapras-frontend/compare/6bb1558553ebae277429fbe1392c48a9e14e2997..2cff5b8acfbda513f9d41f4c2b08e93bc4cb9f11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほどです!!👍
ありがとうございます!!!

onMounted(() => {
if (inputRef.value) inputRef.value.indeterminate = props.indeterminate
})

watch(
() => props.indeterminate,
(value) => {
if (inputRef.value) inputRef.value.indeterminate = value
}
)

return {
inputRef,
onInput,
iconName,
}
},
})
Expand Down Expand Up @@ -73,11 +112,6 @@ export default defineComponent({

.input {
display: none;

&:not(:checked) + .icon-wrap {
transform: translateY(-5px);
opacity: 0;
}
}

.icon-wrap {
Expand All @@ -86,7 +120,26 @@ export default defineComponent({
position: absolute;
top: -2px;
left: 2px;
transition: all 0.3s ease;
transition:
opacity 0.3s ease,
transform 0.3s;
color: $lapras-blue;
transform: translateY(-5px);
opacity: 0;
}

.icon-wrap--visible {
transform: translateY(0);
opacity: 1;
}

.icon-wrap.is-checked,
.icon-wrap.is-indeterminate {
@extend .icon-wrap--visible;
}

.icon-wrap.is-indeterminate {
top: 0;
left: 0;
}
</style>