From 1e702a0f58580d7d8e167904cbe759d82812dd53 Mon Sep 17 00:00:00 2001 From: "fandi.susanto.bts" Date: Wed, 5 Aug 2026 17:42:29 +0700 Subject: [PATCH] Basic Form Components --- src/components/button/types.ts | 4 +- src/components/checkbox/CCheckbox.vue | 217 +++++++++++++++++++ src/components/checkbox/types.ts | 2 + src/components/form-field/CFormField.vue | 112 ++++++++++ src/components/form-field/context.ts | 10 + src/components/form/types.ts | 1 + src/components/form/useFormControl.ts | 23 ++ src/components/input-addon/CInputAddon.vue | 54 +++++ src/components/input-group/CInputGroup.vue | 68 ++++++ src/components/input/CInput.vue | 118 ++++++++++ src/components/number-input/CNumberInput.vue | 171 +++++++++++++++ src/components/password/CPassword.vue | 107 +++++++++ src/components/radio/CRadio.vue | 150 +++++++++++++ src/components/radio/types.ts | 1 + src/components/select/CSelect.vue | 217 +++++++++++++++++++ src/components/select/types.ts | 13 ++ src/components/text-area/CTextArea.vue | 113 ++++++++++ src/index.ts | 21 ++ src/layouts/useLayoutNavigation.ts | 128 +++++++++++ src/pages/components/Checkbox.vue | 129 +++++++++++ src/pages/components/FormField.vue | 79 +++++++ src/pages/components/Input.vue | 75 +++++++ src/pages/components/InputGroup.vue | 123 +++++++++++ src/pages/components/NumberInput.vue | 142 ++++++++++++ src/pages/components/Password.vue | 83 +++++++ src/pages/components/Radio.vue | 86 ++++++++ src/pages/components/Select.vue | 186 ++++++++++++++++ src/pages/components/TextArea.vue | 72 ++++++ src/pages/components/form-demo.scss | 110 ++++++++++ src/router/index.ts | 45 ++++ 30 files changed, 2659 insertions(+), 1 deletion(-) create mode 100644 src/components/checkbox/CCheckbox.vue create mode 100644 src/components/checkbox/types.ts create mode 100644 src/components/form-field/CFormField.vue create mode 100644 src/components/form-field/context.ts create mode 100644 src/components/form/types.ts create mode 100644 src/components/form/useFormControl.ts create mode 100644 src/components/input-addon/CInputAddon.vue create mode 100644 src/components/input-group/CInputGroup.vue create mode 100644 src/components/input/CInput.vue create mode 100644 src/components/number-input/CNumberInput.vue create mode 100644 src/components/password/CPassword.vue create mode 100644 src/components/radio/CRadio.vue create mode 100644 src/components/radio/types.ts create mode 100644 src/components/select/CSelect.vue create mode 100644 src/components/select/types.ts create mode 100644 src/components/text-area/CTextArea.vue create mode 100644 src/pages/components/Checkbox.vue create mode 100644 src/pages/components/FormField.vue create mode 100644 src/pages/components/Input.vue create mode 100644 src/pages/components/InputGroup.vue create mode 100644 src/pages/components/NumberInput.vue create mode 100644 src/pages/components/Password.vue create mode 100644 src/pages/components/Radio.vue create mode 100644 src/pages/components/Select.vue create mode 100644 src/pages/components/TextArea.vue create mode 100644 src/pages/components/form-demo.scss diff --git a/src/components/button/types.ts b/src/components/button/types.ts index 6005542..8d2a0fc 100644 --- a/src/components/button/types.ts +++ b/src/components/button/types.ts @@ -1,3 +1,5 @@ +import type { CFormControlSize } from '../form/types' + export type CButtonVariant = 'default' | 'primary' | 'success' | 'warning' | 'danger' export type CButtonType = 'button' | 'submit' | 'reset' -export type CButtonSize = 'small' | 'medium' | 'large' +export type CButtonSize = CFormControlSize diff --git a/src/components/checkbox/CCheckbox.vue b/src/components/checkbox/CCheckbox.vue new file mode 100644 index 0000000..e8a4e56 --- /dev/null +++ b/src/components/checkbox/CCheckbox.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/src/components/checkbox/types.ts b/src/components/checkbox/types.ts new file mode 100644 index 0000000..357f78d --- /dev/null +++ b/src/components/checkbox/types.ts @@ -0,0 +1,2 @@ +export type CCheckboxValue = string | number | boolean +export type CCheckboxModelValue = CCheckboxValue | null | CCheckboxValue[] diff --git a/src/components/form-field/CFormField.vue b/src/components/form-field/CFormField.vue new file mode 100644 index 0000000..5e3dd03 --- /dev/null +++ b/src/components/form-field/CFormField.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src/components/form-field/context.ts b/src/components/form-field/context.ts new file mode 100644 index 0000000..bc56df0 --- /dev/null +++ b/src/components/form-field/context.ts @@ -0,0 +1,10 @@ +import type { ComputedRef, InjectionKey } from 'vue' + +export interface CFormFieldContext { + controlId: ComputedRef + describedBy: ComputedRef + invalid: ComputedRef + required: ComputedRef +} + +export const cFormFieldKey: InjectionKey = Symbol('c-form-field') diff --git a/src/components/form/types.ts b/src/components/form/types.ts new file mode 100644 index 0000000..dc8bf95 --- /dev/null +++ b/src/components/form/types.ts @@ -0,0 +1 @@ +export type CFormControlSize = 'small' | 'medium' | 'large' diff --git a/src/components/form/useFormControl.ts b/src/components/form/useFormControl.ts new file mode 100644 index 0000000..f2528b0 --- /dev/null +++ b/src/components/form/useFormControl.ts @@ -0,0 +1,23 @@ +import { computed, inject, useAttrs } from 'vue' + +import { cFormFieldKey } from '../form-field/context' + +export function useFormControl(props: { invalid: boolean; required: boolean }) { + const attrs = useAttrs() + const field = inject(cFormFieldKey, undefined) + + const controlId = computed(() => { + const id = attrs.id + return typeof id === 'string' ? id : field?.controlId.value + }) + + const describedBy = computed(() => { + const value = attrs['aria-describedby'] + return typeof value === 'string' ? value : field?.describedBy.value + }) + + const invalid = computed(() => props.invalid || Boolean(field?.invalid.value)) + const required = computed(() => props.required || Boolean(field?.required.value)) + + return { controlId, describedBy, invalid, required } +} diff --git a/src/components/input-addon/CInputAddon.vue b/src/components/input-addon/CInputAddon.vue new file mode 100644 index 0000000..f5a06e8 --- /dev/null +++ b/src/components/input-addon/CInputAddon.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/components/input-group/CInputGroup.vue b/src/components/input-group/CInputGroup.vue new file mode 100644 index 0000000..2305862 --- /dev/null +++ b/src/components/input-group/CInputGroup.vue @@ -0,0 +1,68 @@ + + + diff --git a/src/components/input/CInput.vue b/src/components/input/CInput.vue new file mode 100644 index 0000000..057f68a --- /dev/null +++ b/src/components/input/CInput.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/components/number-input/CNumberInput.vue b/src/components/number-input/CNumberInput.vue new file mode 100644 index 0000000..22568c3 --- /dev/null +++ b/src/components/number-input/CNumberInput.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/src/components/password/CPassword.vue b/src/components/password/CPassword.vue new file mode 100644 index 0000000..44d71a1 --- /dev/null +++ b/src/components/password/CPassword.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/components/radio/CRadio.vue b/src/components/radio/CRadio.vue new file mode 100644 index 0000000..44a2bd4 --- /dev/null +++ b/src/components/radio/CRadio.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/src/components/radio/types.ts b/src/components/radio/types.ts new file mode 100644 index 0000000..5ea2c39 --- /dev/null +++ b/src/components/radio/types.ts @@ -0,0 +1 @@ +export type CRadioValue = string | number | boolean diff --git a/src/components/select/CSelect.vue b/src/components/select/CSelect.vue new file mode 100644 index 0000000..61e38c3 --- /dev/null +++ b/src/components/select/CSelect.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/src/components/select/types.ts b/src/components/select/types.ts new file mode 100644 index 0000000..2be95bc --- /dev/null +++ b/src/components/select/types.ts @@ -0,0 +1,13 @@ +export type CSelectPrimitive = string | number | boolean +export type CSelectValue = CSelectPrimitive | object + +export interface CSelectOption { + label: string + value: CSelectValue + disabled?: boolean + hidden?: boolean +} + +export type CSelectLabelAccessor = string | ((option: object) => string) +export type CSelectKeyAccessor = string | ((option: object) => string | number) +export type CSelectValueAccessor = string | ((option: object) => CSelectValue) diff --git a/src/components/text-area/CTextArea.vue b/src/components/text-area/CTextArea.vue new file mode 100644 index 0000000..73f8294 --- /dev/null +++ b/src/components/text-area/CTextArea.vue @@ -0,0 +1,113 @@ + + +