Skip to content

Commit

Permalink
feat: new data and validation (#423)
Browse files Browse the repository at this point in the history
  • Loading branch information
kiaking committed Dec 21, 2023
1 parent f5792e7 commit 8b7f4f6
Show file tree
Hide file tree
Showing 29 changed files with 322 additions and 73 deletions.
20 changes: 1 addition & 19 deletions docs/validation/validators.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Validators

The `Validators` provides set of functions to validate values. Sefirot encourages to use builtin validatoes provided by [Vuelidate](https://github.com/vuelidate/vuelidate) library. These validators are which is not provided by Vuelidate.
The `Validators` provides set of functions to validate values.

You may import functions from `validation/validators`.

Expand All @@ -17,8 +17,6 @@ function checked(value: boolean): boolean
```

```ts
import { checked } from '@globalbrain/sefirot/lib/validation/validators'
checked(false)
```

Expand All @@ -31,8 +29,6 @@ function fileExtension(file: File, extensions: string[]): boolean
```

```ts
import { fileExtension } from '@globalbrain/sefirot/lib/validation/validators'
fileExtension(file, ['jpg', 'png'])
```

Expand All @@ -50,8 +46,6 @@ function hms(
```

```ts
import { hms } from '@globalbrain/sefirot/lib/validation/validators'
const time = {
hour: '10',
minute: '61', // Invalid value.
Expand Down Expand Up @@ -82,8 +76,6 @@ function maxFileSize(file: File, size: string): boolean
```

```ts
import { maxFileSize } from '@globalbrain/sefirot/lib/validation/validators'
maxFileSize(file, '100mb')
```

Expand All @@ -96,8 +88,6 @@ function maxTotalFileSize(files: File[], size: string): boolean
```

```ts
import { maxTotalFileSize } from '@globalbrain/sefirot/lib/validation/validators'
maxTotalFileSize([fileA, fileB], '100mb')
```

Expand All @@ -110,8 +100,6 @@ function month(value: number): boolean
```

```ts
import { month } from '@globalbrain/sefirot/lib/validation/validators'
month(13) // <- false
```

Expand All @@ -129,8 +117,6 @@ function requiredHms(
```

```ts
import { requiredHms } from '@globalbrain/sefirot/lib/validation/validators'
const time = {
hour: '10',
minute: null, // Value missing.
Expand Down Expand Up @@ -166,8 +152,6 @@ function requiredYmd(
```

```ts
import { requiredYmd } from '@globalbrain/sefirot/lib/validation/validators'
const date = {
year: 1985,
month: null, // Value missing.
Expand Down Expand Up @@ -203,8 +187,6 @@ function ymd(
```

```ts
import { ymd } from '@globalbrain/sefirot/lib/validation/validators'
const date = {
year: 1985,
month: 15, // Invalid value.
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import IconQuestion from '@iconify-icons/ph/question'
import { type DefineComponent, computed, unref, useSlots } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SIcon from './SIcon.vue'
import STooltip from './STooltip.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type IconifyIcon } from '@iconify/vue/dist/offline'
import IconCheck from '@iconify-icons/ph/check-bold'
import IconMinus from '@iconify-icons/ph/minus-bold'
import { computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SIcon from './SIcon.vue'
import SInputBase from './SInputBase.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputCheckboxes.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
import SInputCheckbox from './SInputCheckbox.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputDate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { DatePicker } from 'v-calendar'
import { computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import { type Day, day } from '../support/Day'
import SInputBase from './SInputBase.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import xor from 'lodash-es/xor'
import { type DefineComponent, computed, ref } from 'vue'
import { type DropdownSectionFilter, useManualDropdownPosition } from '../composables/Dropdown'
import { useFlyout } from '../composables/Flyout'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import { isArray } from '../support/Utils'
import SDropdown from './SDropdown.vue'
import SIcon from './SIcon.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputFile.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed, ref } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputHMS.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed, ref } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type IconifyIcon } from '@iconify/vue/dist/offline'
import IconImage from '@iconify-icons/ph/image-bold'
import { computed, ref } from 'vue'
import { useImageSrcFromFile } from '../composables/Image'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SButton from './SButton.vue'
import SIcon from './SIcon.vue'
import SInputBase from './SInputBase.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputNumber.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import { isNullish, isString } from '../support/Utils'
import SInputText from './SInputText.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputRadio.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputRadios.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
import SInputRadio from './SInputRadio.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputSegments.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts" generic="T extends string | number | boolean">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
import SInputSegmentsOption, { type Mode } from './SInputSegmentsOption.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type IconifyIcon } from '@iconify/vue/dist/offline'
import IconCaretDown from '@iconify-icons/ph/caret-down-bold'
import IconCaretUp from '@iconify-icons/ph/caret-up-bold'
import { type DefineComponent, computed, ref } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SIcon from './SIcon.vue'
import SInputBase from './SInputBase.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputSwitch.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputSwitches.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
import SInputSwitch from './SInputSwitch.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputText.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed, ref } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import { isString } from '../support/Utils'
import SIcon from './SIcon.vue'
import SInputBase from './SInputBase.vue'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputTextarea.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { computed } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SInputYMD.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { type IconifyIcon } from '@iconify/vue/dist/offline'
import { type DefineComponent, computed, ref } from 'vue'
import { type Validatable } from '../composables/Validation'
import { type Validatable } from '../composables/V'
import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
Expand Down
8 changes: 4 additions & 4 deletions lib/composables/Card.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { type InjectionKey, type Ref, inject, provide, ref } from 'vue'
import { type Ref, inject, provide, ref } from 'vue'

export interface CardState {
isCollapsed: Ref<boolean>
setCollapse(value: boolean): void
toggleCollapse(): void
}

export const cardStateKey = Symbol('card-state') as InjectionKey<CardState>
export const CardStateKey = 'card-state'

export function provideCardState(): CardState {
const isCollapsed = ref(false)
Expand All @@ -25,13 +25,13 @@ export function provideCardState(): CardState {
toggleCollapse
}

provide(cardStateKey, cardState)
provide(CardStateKey, cardState)

return cardState
}

export function useCardState(): CardState {
const cardState = inject(cardStateKey)
const cardState = inject<CardState | null>(CardStateKey, null)

if (!cardState) {
throw new Error(
Expand Down
21 changes: 21 additions & 0 deletions lib/composables/D.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type Ref, ref } from 'vue'

export interface D<T extends Record<string, any>> {
data: Ref<T>
init(): void
}

export function useD<T extends Record<string, any>>(data: T): D<T> {
const initialData = JSON.parse(JSON.stringify(data))

const refData = ref(data) as Ref<T>

function init(): void {
refData.value = initialData
}

return {
data: refData,
init
}
}
4 changes: 4 additions & 0 deletions lib/composables/Data.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* @deprecated Use `composables/D` module instead.
*/

import { watchOnce } from '@vueuse/core'
import cloneDeep from 'lodash-es/cloneDeep'
import { type WatchSource, reactive } from 'vue'
Expand Down
4 changes: 4 additions & 0 deletions lib/composables/Form.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* @deprecated Use `composables/D` and `composables/V` modules instead.
*/

import { type Ref, computed, reactive, toRefs } from 'vue'
import { type Snackbar, useSnackbars } from '../stores/Snackbars'
import { type UseDataInput, useData } from './Data'
Expand Down
73 changes: 73 additions & 0 deletions lib/composables/V.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { type Validation, type ValidationArgs, useVuelidate } from '@vuelidate/core'
import { type MaybeRefOrGetter, type Ref, computed, toValue } from 'vue'
import { type Snackbar, useSnackbars } from '../stores/Snackbars'
import { useTrans } from './Lang'

export interface V<
Data extends { [key in keyof Rules]: any },
Rules extends ValidationArgs = ValidationArgs
> {
validation: Ref<Validation<Rules, Data>>
validate(): Promise<boolean>
validateAndNotify(message?: Snackbar): Promise<boolean>
reset(): void
}

export interface Validatable {
readonly $dirty: boolean
readonly $invalid: boolean
readonly $errors: ValidatableError[]
readonly $touch: () => void
}

export interface ValidatableError {
readonly $message: string | Ref<string>
}

export function useV<
Data extends { [key in keyof Rules]: any },
Rules extends ValidationArgs = ValidationArgs
>(
data: MaybeRefOrGetter<Data>,
rules: MaybeRefOrGetter<Rules>
): V<Data, Rules> {
const { t } = useTrans({
en: { notify: 'Form contains errors. Please correct them and try again.' },
ja: { notify: 'フォームにエラーがあります。内容を確認し、再度お試しください。' }
})

const snackbars = useSnackbars()

const d = computed(() => toValue(data))
const r = computed(() => toValue(rules))

const validation = useVuelidate(r, d)

function reset(): void {
validation.value.$reset()
}

function validate(): Promise<boolean> {
return validation.value.$validate()
}

async function validateAndNotify(message?: Snackbar): Promise<boolean> {
const valid = await validate()

if (!valid) {
snackbars.push(message ?? {
mode: 'danger',
text: t.notify
})
}

return valid
}

return {
validation,
validate,
validateAndNotify,
reset
}
}

0 comments on commit 8b7f4f6

Please sign in to comment.