Ninja Button button
A button that creates shadow clones when disabled ((-∀(-∀-)∀-))
Usage Examples
Basic Usage
Fake! They're all fake! ((((;゚Д゚)))
View example source code
vue
<template>
<div class="w-full flex flex-col gap-4">
<base-checkbox
v-model="disabled"
:label="t('disableButton')"
class="border rounded p-4"
/>
<div class="h-[50vh] flex items-center justify-center">
<btn-ninja
:label="t('submit')"
:disabled
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import BaseCheckbox from '../../base-checkbox.vue'
import BtnNinja from '../btn-ninja.vue'
const { t } = useI18n()
const disabled = ref(false)
</script>Careful Confirmation
Eliminate all shadow clones before you can click "Confirm". ( •̀ ω •́ )✧
Please confirm your action!You are about to enable the "Auto-delete all Monday morning meetings" feature. This will:
- Permanently delete all Monday morning meetings from the calendar
- Cancel all related meeting room reservations
- Automatically send an "I hate Mondays" email to all participants
Cancel
🎉 Operation completed!
View example source code
vue
<template>
<div class="relative flex flex-col gap-3 border border-red-300 rounded-lg bg-red-100/50 p-6">
<span class="text-xl text-red-800 font-bold">
{{ t('title') }}
</span>
<span class="mt-2 text-red-700">
{{ t('description') }}
</span>
<ul class="list-disc list-inside text-sm text-red-400 !m-0">
<li>{{ t('action1') }}</li>
<li>{{ t('action2') }}</li>
<li>{{ t('action3') }}</li>
</ul>
<div class="flex justify-end gap-4">
<btn-ninja>
<div class="cursor-pointer p-2 px-4 text-gray-800">
{{ t('cancel') }}
</div>
</btn-ninja>
<btn-ninja
v-slot="{ isHidden }"
:disabled
@show="disabled = false"
>
<base-btn
:label="t('confirm')"
class="active:scale-95 !border-red-600"
:class="{
'text-red-600 !border-dashed': disabled || isHidden,
'!bg-red-600/50 !text-white': !disabled,
}"
@click="handleClick"
/>
</btn-ninja>
</div>
<div
class="pointer-events-none absolute inset-0 flex items-center justify-center bg-black/90 duration-500"
:class="done ? ' opacity-100' : 'opacity-0'"
>
<span class="text-2xl text-white font-bold">
{{ t('done') }}
</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import BaseBtn from '../../base-btn.vue'
import BtnNinja from '../btn-ninja.vue'
const { t } = useI18n()
const disabled = ref(true)
const done = ref(false)
function handleClick() {
if (disabled.value) {
return
}
done.value = true
setTimeout(() => {
done.value = false
disabled.value = true
}, 2000)
}
</script>How It Works
Anyone with some Vue experience probably knows that Vue components can use slots to let users insert custom template content.
One day, a thought suddenly popped up: what happens if the same slot is used multiple times? ヾ(◍'౪`◍)ノ゙
And that's how this component was born. ᕕ( ゚ ∀。)ᕗ
Source Code
API
Props
interface Props {
label?: string;
/** 分身數量 */
count?: number;
disabled?: boolean;
}Emits
interface Emits {
click: [];
hidden: [];
show: [];
}Slots
interface Slots {
default?: (props: { isHidden: boolean }) => unknown;
}