Skip to content

Commit

Permalink
WSelect
Browse files Browse the repository at this point in the history
  • Loading branch information
Woolim Park committed Nov 21, 2023
1 parent b158d12 commit 3c52f34
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 52 deletions.
47 changes: 29 additions & 18 deletions components/WInput/WInput.story.vue
Original file line number Diff line number Diff line change
@@ -1,38 +1,49 @@
<script setup>
<script setup lang="ts">
import WInput from "./WInput.vue";
import { InputType } from "@/types";
import { reactive } from "vue";
import { CurrencyEuroIcon } from "@heroicons/vue/24/outline";
const state = reactive({
type: InputType.TEXT,
value: "",
label: "What are you looking for?",
});
const onInput = (e: InputEvent) => {
const target = e.target as HTMLInputElement;
state.value = target?.value;
};
</script>

<template>
<Story title="WInput" :layout="{ type: 'grid', width: 300 }">
<Story title="Form/WInput" :layout="{ type: 'grid', width: 300 }">
<Variant title="default">
<WInput
placeholder="Type here..."
v-model="state.value"
:disabled="state.disabled"
:type="state.type"
:label="state.label"
/>
<WInput placeholder="Type here..." :value="state.value" @input="onInput" :type="state.type" />
</Variant>
<Variant title="error">
<WInput
:label="state.label"
placeholder="Type here..."
v-model="state.value"
error
:type="state.type"
error-message="Error message"
/>
<WInput placeholder="Type here..." :value="state.value" @input="onInput" error :type="state.type" />
</Variant>
<Variant title="disabled">
<WInput :label="state.label" placeholder="Type here..." v-model="state.value" disabled :type="state.type" />
<WInput placeholder="Type here..." :value="state.value" @input="onInput" disabled :type="state.type" />
</Variant>
<Variant title="icon">
<WInput placeholder="Type here..." :value="state.value" @input="onInput" :type="state.type">
<template #append>
<CurrencyEuroIcon />
</template>
</WInput>
</Variant>
<Variant title="append text">
<WInput placeholder="Type here..." :value="state.value" @input="onInput" :type="state.type">
<template #append> € </template>
</WInput>
</Variant>
<Variant title="disabled with icon">
<WInput placeholder="Type here..." disabled :value="state.value" @input="onInput" :type="state.type">
<template #append>
<CurrencyEuroIcon />
</template>
</WInput>
</Variant>
</Story>
</template>
58 changes: 38 additions & 20 deletions components/WInput/WInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,69 @@
import WSheep from "@/components/WSheep";
import { InputType } from "@/types";
import { useMeh } from "@/composables/useMeh";
import { toRef, PropType, onUnmounted } from "vue";
import { ref, toRef, PropType, onUnmounted } from "vue";
import { useIds } from "@/composables/useIds";
import WLabel from "@/components/WLabel";
import WError from "@/components/WError";
const props = defineProps({
label: { type: String, default: "" },
modelValue: { type: String, default: "" },
value: { type: [String, Number], default: "" },
type: { type: String as PropType<InputType>, default: InputType.TEXT },
disabled: { type: Boolean, default: false },
placeholder: { type: String, default: undefined },
error: { type: Boolean, default: false },
errorMessage: { type: String, default: "" },
});
const emit = defineEmits(["update:modelValue"]);
const id = useIds().get();
const { isPlaying, playMeh, deletePlaying } = useMeh({ disabled: toRef(props, "disabled"), id });
const onInput = (e: Event) => {
const { target } = e;
if (target) {
emit("update:modelValue", (target as HTMLInputElement).value);
}
const emit = defineEmits(["click", "click:prepend"]);
const onClick = (e: Event) => {
if (props.disabled) return;
emit("click", e);
playMeh();
};
const onPrependClick = (e: Event) => {
if (props.disabled) return;
emit("click", e);
emit("click:prepend", e);
playMeh();
};
onUnmounted(() => {
deletePlaying();
});
defineOptions({
inheritAttrs: false,
});
</script>
<template>
<div>
<WLabel :for="id" v-if="label" :disabled="disabled"> {{ label }}</WLabel>
<div class="ml-[20px] my-1 mr-1 relative">
<WSheep class="absolute left-[-20px] top-[50%] -translate-y-1/2" :disabled="disabled" :meh="isPlaying" />
<div class="ml-[20px] my-1 mr-1 relative">
<WSheep class="absolute left-[-20px] top-[50%] -translate-y-1/2" :disabled="disabled" :meh="isPlaying" />
<div
class="flex cursor-pointer items-center justify-between focus-within:wc-focus wc-rounded wc-bg wc-border wc-text"
:class="{
['wc-error-border']: error || errorMessage,
['wc-disabled-text wc-disabled-bg wc-disabled-border cursor-not-allowed']: disabled,
}"
>
<input
ref="input"
class="w-full cursor-pointer bg-inherit py-3 pl-6 pr-3 wc-rounded outline-none disabled:cursor-not-allowed"
:class="{ ['pr-1']: $slots.append }"
:id="id"
v-bind="$attrs"
:type="type"
class="py-3 w-full focus:wc-focus pl-6 pr-3 wc-bg wc-border wc-rounded disabled:wc-disabled-text disabled:wc-disabled-bg disabled:wc-disabled-border disabled:cursor-not-allowed wc-text"
:class="{ ['wc-error-border']: error || errorMessage }"
:placeholder="placeholder"
:disabled="disabled"
:value="modelValue"
@input="onInput"
@focus="playMeh"
:value="value"
@click="onClick"
/>

<span
v-if="$slots.append"
@click="onPrependClick"
class="w-[30px] mx-1 h-[30px] text-[16px] flex justify-center items-center"
>
<slot name="append"> </slot>
</span>
</div>
<WError :error-message="errorMessage" />
</div>
</template>
38 changes: 38 additions & 0 deletions components/WSelect/WSelect.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup>
import WSelect from "./WSelect.vue";
import { reactive } from "vue";
const state = reactive({
modelValue: undefined,
options: [
{ text: "Option 1", value: 1 },
{ text: "Option 2", value: 2 },
{ text: "Option 3", value: 3 },
{ text: "Option 4", value: 4 },
{ text: "Option 5", value: 5 },
{ text: "Option 6", value: 6 },
],
label: "Select an option",
placeholder: "Any option?",
});
</script>

<template>
<Story title="Form/WSelect" :layout="{ type: 'grid', width: 300 }">
<Variant title="default">
<WSelect v-model="state.value" :label="state.label" :options="state.options" :placeholder="state.placeholder" />
</Variant>
<Variant title="disabled">
<WSelect
v-model="state.value"
:label="state.label"
:options="state.options"
:placeholder="state.placeholder"
disabled
/>
</Variant>
<Variant title="bottom">
<WSelect v-model="state.value" :label="state.label" :options="state.options" :placeholder="state.placeholder" />
</Variant>
</Story>
</template>
106 changes: 106 additions & 0 deletions components/WSelect/WSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<script setup lang="ts">
import WLabel from "@/components/WLabel";
import { useIds } from "@/composables/useIds";
import { IOption } from "@/types";
import { PropType, ref } from "vue";
import WInput from "@/components/WInput";
import { ChevronDownIcon } from "@heroicons/vue/24/outline";
import { vOnClickOutside } from "@vueuse/components";
import { useDropdown } from "@/composables/useDropdown";
const props = defineProps({
label: { type: String, default: "" },
modelValue: { type: [String, Number], default: "" },
options: { type: Array as PropType<IOption[]>, required: true },
placeholder: { type: String, default: "" },
disabled: { type: Boolean, default: false },
});
const emit = defineEmits(["update:modelValue"]);
const id = useIds().get();
const active = ref(false);
const onToggle = () => {
if (props.disabled) return;
active.value = !active.value;
};
const onSelect = (value: number | string) => {
if (props.disabled) return;
emit("update:modelValue", value);
active.value = false;
};
const input = ref<HTMLElement>();
const dropdown = ref<HTMLElement>();
const { isDirectionUpwards, dropdownBottom, dropdownLeft, dropdownTop, dropdownWidth } = useDropdown({
rootEl: input,
dropdownEl: dropdown,
show: active,
});
</script>
<template>
<div v-on-click-outside="() => (active = false)">
<WLabel :for="id" :disabled="disabled">{{ label }}</WLabel>
<WInput
@click="onToggle"
ref="input"
:value="modelValue"
:id="id"
:placeholder="placeholder"
readonly
:disabled="disabled"
>
<template #append>
<ChevronDownIcon
class="transition-transform duration-200 w-[30px] h-[30px]"
:class="{ ['rotate-180']: active }"
/>
</template>
</WInput>

<ul
ref="dropdown"
v-show="active"
:id="id"
class="wc-rounded z-[1] max-h-[196px] overflow-y-auto wc-border flex flex-col p-1 gap-2 bg-white"
:class="$style.dropdown"
>
<li v-for="o in options">
<button
@click="onSelect(o.value)"
:class="{ ['!text-sky-600 font-[500]']: o.value === modelValue }"
class="bg-white w-full rounded-[8px] text-left focus:wc-focus focus:wc-hover-bg hover:wc-hover-bg !border-none px-3 py-2 wc-text"
>
{{ o.text }}
</button>
</li>
</ul>
</div>
</template>
<style module>
.dropdown {
position: fixed;
bottom: v-bind("isDirectionUpwards ? dropdownBottom : dropdownTop");
left: v-bind("dropdownLeft");
width: v-bind("dropdownWidth");
min-width: 0;
/* width */
&::-webkit-scrollbar {
width: 8px;
}
/* Track */
&::-webkit-scrollbar-track {
@apply shadow-gray-50;
box-shadow: inset 0 0 1px;
border-radius: 20px;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: black;
border-radius: 20px;
}
}
</style>
1 change: 1 addition & 0 deletions components/WSelect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./WSelect.vue";
36 changes: 36 additions & 0 deletions components/WTextField/WTextField.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script setup>
import WTextField from "./WTextField.vue";
import { reactive } from "vue";
const state = reactive({
value: "",
label: "What are you looking for?",
});
</script>

<template>
<Story title="Form/WTextField" :layout="{ type: 'grid', width: 300 }">
<Variant title="default">
<WTextField placeholder="Type here..." v-model="state.value" :disabled="state.disabled" :label="state.label" />
</Variant>
<Variant title="disabled">
<WTextField
placeholder="Type here..."
disabled
v-model="state.value"
:disabled="state.disabled"
:label="state.label"
/>
</Variant>
<Variant title="error">
<WTextField
placeholder="Type here..."
error
error-message="Some error happens"
v-model="state.value"
:disabled="state.disabled"
:label="state.label"
/>
</Variant>
</Story>
</template>
41 changes: 41 additions & 0 deletions components/WTextField/WTextField.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script setup lang="ts">
import WSheep from "@/components/WSheep";
import { InputType } from "@/types";
import { useMeh } from "@/composables/useMeh";
import { toRef, PropType, onUnmounted, computed } from "vue";
import { useIds } from "@/composables/useIds";
import WLabel from "@/components/WLabel";
import WError from "@/components/WError";
import WInput from "@/components/WInput";
const props = defineProps({
label: { type: String, default: "" },
modelValue: { type: String, default: "" },
type: { type: String as PropType<InputType>, default: InputType.TEXT },
disabled: { type: Boolean, default: false },
placeholder: { type: String, default: undefined },
error: { type: Boolean, default: false },
errorMessage: { type: String, default: "" },
});
const emit = defineEmits(["update:modelValue"]);
const id = useIds().get();
const onInput = (e: InputEvent) => {
const target = e.target;
emit("update:modelValue", target.value);
};
</script>

<template>
<div>
<WLabel :for="id" v-if="label" :disabled="disabled"> {{ label }}</WLabel>
<WInput
:value="modelValue"
:id="id"
@input="onInput"
:placeholder="placeholder"
:disabled="disabled"
:error="error"
/>
<WError v-if="errorMessage" :error-message="errorMessage" />
</div>
</template>
1 change: 1 addition & 0 deletions components/WTextField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./WTextField.vue";
1 change: 1 addition & 0 deletions components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { default as WRadioGroup } from "./WRadioGroup";
export { default as WLabel } from "./WLabel";
export { default as WError } from "./WError";
export { default as WNavbar } from "./WNavbar";
export { default as WTextField } from "./WTextField";
Loading

0 comments on commit 3c52f34

Please sign in to comment.