@globalbrain/sefirot 2.23.0 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, nextTick, ref, watch } from 'vue'
2
+ import { computed, nextTick, shallowRef, watch } from 'vue'
3
3
  import type { LinkCallback, LinkSubscriberPayload } from '../composables/Markdown'
4
4
  import { useLink, useMarkdown } from '../composables/Markdown'
5
5
 
@@ -14,7 +14,7 @@ const emit = defineEmits<{
14
14
  (e: 'clicked', payload: LinkSubscriberPayload): void
15
15
  }>()
16
16
 
17
- const container = ref<Element | null>(null)
17
+ const container = shallowRef<Element | null>(null)
18
18
 
19
19
  const { addListeners, subscribe } = useLink({
20
20
  container,
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, reactive, ref, toRefs, watch } from 'vue'
2
+ import { computed, reactive, shallowRef, toRefs, watch } from 'vue'
3
3
  import type { Table } from '../composables/Table'
4
4
  import SSpinner from './SSpinner.vue'
5
5
  import STableCell from './STableCell.vue'
@@ -29,8 +29,8 @@ const {
29
29
  onReset
30
30
  } = toRefs(props.options)
31
31
 
32
- const head = ref<HTMLElement | null>(null)
33
- const body = ref<HTMLElement | null>(null)
32
+ const head = shallowRef<HTMLElement | null>(null)
33
+ const body = shallowRef<HTMLElement | null>(null)
34
34
 
35
35
  let headLock = false
36
36
  let bodyLock = false
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref } from 'vue'
2
+ import { computed, shallowRef } from 'vue'
3
3
  import type { Position } from '../composables/Tooltip'
4
4
  import { useTooltip } from '../composables/Tooltip'
5
5
  import SMarkdown from './SMarkdown.vue'
@@ -10,8 +10,8 @@ const props = defineProps<{
10
10
  position?: Position
11
11
  }>()
12
12
 
13
- const tip = ref<HTMLElement | null>(null)
14
- const content = ref<HTMLElement | null>(null)
13
+ const tip = shallowRef<HTMLElement | null>(null)
14
+ const content = shallowRef<HTMLElement | null>(null)
15
15
  const classes = computed(() => [props.position ?? 'top'])
16
16
 
17
17
  const { on, show, hide } = useTooltip(
@@ -1,10 +1,7 @@
1
- import type { Ref } from 'vue'
2
- import { computed } from 'vue'
1
+ import { type Ref, computed, reactive, toRefs } from 'vue'
3
2
  import { useSnackbars } from '../stores/Snackbars'
4
- import type { UseDataInput } from './Data'
5
- import { useData } from './Data'
6
- import type { Validation } from './Validation'
7
- import { useValidation } from './Validation'
3
+ import { type UseDataInput, useData } from './Data'
4
+ import { type Validation, useValidation } from './Validation'
8
5
 
9
6
  export interface Form<T extends Record<string, any>> {
10
7
  data: T
@@ -15,25 +12,49 @@ export interface Form<T extends Record<string, any>> {
15
12
  validateAndNotify(): Promise<boolean>
16
13
  }
17
14
 
18
- export interface UseFormOptions<T extends Record<string, any>> {
19
- data: UseDataInput<T>
20
- rules?: Record<string, any> | ((state: T) => Record<string, any>)
15
+ export type ComputedData<T extends Record<string, () => any>> = {
16
+ [K in keyof T]: ReturnType<T[K]>
17
+ }
18
+
19
+ export type AllData<
20
+ D extends Record<string, any>,
21
+ C extends Record<string, () => any>
22
+ > = D & ComputedData<C>
23
+
24
+ export interface UseFormOptions<
25
+ D extends Record<string, any>,
26
+ C extends Record<string, () => any>
27
+ > {
28
+ data: UseDataInput<D>
29
+ computed?: (data: D) => C
30
+ rules?: Record<string, any> | ((data: AllData<D, C>) => Record<string, any>)
21
31
  }
22
32
 
23
33
  export function useForm<
24
- T extends Record<string, any>
25
- >(options: UseFormOptions<T>): Form<T> {
34
+ D extends Record<string, any> = Record<string, any>,
35
+ C extends Record<string, () => any> = Record<string, () => any>
36
+ >(options: UseFormOptions<D, C>): Form<AllData<D, C>> {
26
37
  const snackbars = useSnackbars()
27
38
 
28
39
  const data = useData(options.data)
40
+ const dataStateRef = toRefs(data.state)
41
+
42
+ const computedData = options.computed
43
+ ? createComputedData(options.computed(data.state))
44
+ : {}
45
+
46
+ const allData = reactive({
47
+ ...dataStateRef,
48
+ ...computedData
49
+ }) as AllData<D, C>
29
50
 
30
51
  const rules = computed(() => {
31
52
  return options.rules
32
- ? typeof options.rules === 'function' ? options.rules(data.state) : options.rules
53
+ ? typeof options.rules === 'function' ? options.rules(allData) : options.rules
33
54
  : {}
34
55
  })
35
56
 
36
- const validation = useValidation(data.state, rules)
57
+ const validation = useValidation(allData, rules)
37
58
 
38
59
  function init(): void {
39
60
  data.init()
@@ -62,7 +83,7 @@ export function useForm<
62
83
  }
63
84
 
64
85
  return {
65
- data: data.state,
86
+ data: allData,
66
87
  init,
67
88
  reset,
68
89
  validation,
@@ -70,3 +91,15 @@ export function useForm<
70
91
  validateAndNotify
71
92
  }
72
93
  }
94
+
95
+ function createComputedData<
96
+ C extends Record<string, () => any>
97
+ >(input: C): ComputedData<C> {
98
+ const computedData = {} as any
99
+
100
+ for (const [key, fn] of Object.entries(input)) {
101
+ computedData[key] = computed(() => fn())
102
+ }
103
+
104
+ return computedData
105
+ }
@@ -4,13 +4,31 @@ import PluginRelativeTime from 'dayjs/plugin/relativeTime'
4
4
  import PluginTimezone from 'dayjs/plugin/timezone'
5
5
  import PluginUtc from 'dayjs/plugin/utc'
6
6
 
7
- export type Day = Dayjs
8
- export type Input = ConfigType
9
-
10
7
  dayjs.extend(PluginUtc)
11
8
  dayjs.extend(PluginTimezone)
12
9
  dayjs.extend(PluginRelativeTime)
13
10
 
11
+ export type Day = Dayjs
12
+ export type Input = ConfigType
13
+
14
+ /**
15
+ * The year, month, and date object interface.
16
+ */
17
+ export interface Ymd {
18
+ year: number | null
19
+ month: number | null
20
+ date: number | null
21
+ }
22
+
23
+ /**
24
+ * The hour, minute, and second object interface.
25
+ */
26
+ export interface Hms {
27
+ hour: string | null
28
+ minute: string | null
29
+ second: string | null
30
+ }
31
+
14
32
  export function day(input?: Input): Day {
15
33
  return dayjs(input)
16
34
  }
@@ -22,3 +40,33 @@ export function utc(input?: Input): Day {
22
40
  export function tz(input?: Input, timezone?: string): Day {
23
41
  return dayjs.tz(input, timezone)
24
42
  }
43
+
44
+ /**
45
+ * Creates a new `Ymd` object.
46
+ */
47
+ export function createYmd(
48
+ year: number | null = null,
49
+ month: number | null = null,
50
+ date: number | null = null
51
+ ): Ymd {
52
+ return {
53
+ year,
54
+ month,
55
+ date
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Creates a new `Hms` object.
61
+ */
62
+ export function createHms(
63
+ hour: string | null = null,
64
+ minute: string | null = null,
65
+ second: string | null = null
66
+ ): Hms {
67
+ return {
68
+ hour,
69
+ minute,
70
+ second
71
+ }
72
+ }
@@ -1,7 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
- import type { Hms, HmsType } from '../validators/hms'
2
+ import type { Hms } from '../../support/Day'
3
3
  import { hms as baseHms } from '../validators/hms'
4
4
 
5
+ type HmsType = 'h' | 'm' | 's'
6
+
5
7
  export function hms(required?: HmsType[], msg?: string) {
6
8
  return helpers.withMessage(
7
9
  () => msg ?? 'The time is invalid.',
@@ -5,6 +5,7 @@ export * from './email'
5
5
  export * from './fileExtension'
6
6
  export * from './hms'
7
7
  export * from './maxFileSize'
8
+ export * from './maxTotalFileSize'
8
9
  export * from './maxLength'
9
10
  export * from './maxValue'
10
11
  export * from './minLength'
@@ -0,0 +1,16 @@
1
+ import { helpers } from '@vuelidate/validators'
2
+ import { maxTotalFileSize as baseMaxTotalFileSize } from '../validators/maxTotalFileSize'
3
+
4
+ export function maxTotalFileSize(size: string, msg?: string) {
5
+ return helpers.withParams(
6
+ { size },
7
+ helpers.withMessage(
8
+ ({ $params }) => {
9
+ return msg ?? `The total file size must be smaller than ${$params.size}.`
10
+ },
11
+ (files: File[]) => {
12
+ return !helpers.req(files) || baseMaxTotalFileSize(files, size)
13
+ }
14
+ )
15
+ )
16
+ }
@@ -1,7 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
- import type { Hms, HmsType } from '../validators/requiredHms'
2
+ import type { Hms } from '../../support/Day'
3
3
  import { requiredHms as baseRequiredHms } from '../validators/requiredHms'
4
4
 
5
+ type HmsType = 'h' | 'm' | 's'
6
+
5
7
  export function requiredHms(required?: HmsType[], msg?: string) {
6
8
  return helpers.withMessage(
7
9
  () => msg ?? 'The field is required.',
@@ -1,7 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
- import type { Ymd, YmdType } from '../validators/requiredYmd'
2
+ import type { Ymd } from '../../support/Day'
3
3
  import { requiredYmd as baseRequiredYmd } from '../validators/requiredYmd'
4
4
 
5
+ type YmdType = 'y' | 'm' | 'd'
6
+
5
7
  export function requiredYmd(required?: YmdType[], msg?: string) {
6
8
  return helpers.withMessage(
7
9
  () => msg ?? 'The field is required.',
@@ -1,7 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
- import type { Ymd, YmdType } from '../validators/ymd'
2
+ import type { Ymd } from '../../support/Day'
3
3
  import { ymd as baseYmd } from '../validators/ymd'
4
4
 
5
+ type YmdType = 'y' | 'm' | 'd'
6
+
5
7
  export function ymd(required?: YmdType[], msg?: string) {
6
8
  return helpers.withMessage(
7
9
  () => msg ?? 'The date is invalid.',
@@ -1,12 +1,8 @@
1
- export interface Hms {
2
- hour?: string | null
3
- minute?: string | null
4
- second?: string | null
5
- }
1
+ import type { Hms } from '../../support/Day'
6
2
 
7
- export type HmsType = 'h' | 'm' | 's'
3
+ type HmsType = 'h' | 'm' | 's'
8
4
 
9
- export const HmsMap = {
5
+ const HmsMap = {
10
6
  h: 'hour',
11
7
  m: 'minute',
12
8
  s: 'second'
@@ -16,7 +12,7 @@ export function hms(hms: Hms, required: HmsType[] = ['h', 'm', 's']): boolean {
16
12
  return required.every((r) => {
17
13
  const value = hms[HmsMap[r]]
18
14
 
19
- if (value === undefined) {
15
+ if (value === null) {
20
16
  return true
21
17
  }
22
18
 
@@ -0,0 +1,9 @@
1
+ export * from './checked'
2
+ export * from './fileExtension'
3
+ export * from './hms'
4
+ export * from './maxFileSize'
5
+ export * from './maxTotalFileSize'
6
+ export * from './month'
7
+ export * from './requiredHms'
8
+ export * from './requiredYmd'
9
+ export * from './ymd'
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Validates if the total size of the given files is smaller than the
3
+ * given size.
4
+ */
5
+ export function maxTotalFileSize(files: File[], size: string): boolean {
6
+ const factor = /gb/i.test(size)
7
+ ? 1e9
8
+ : /mb/i.test(size)
9
+ ? 1e6
10
+ : /kb/i.test(size)
11
+ ? 1e3
12
+ : 1
13
+
14
+ const total = files.reduce((total, file) => total + file.size, 0)
15
+
16
+ return total <= factor * +size.replace(/[^\d\.]/g, '')
17
+ }
@@ -1,12 +1,8 @@
1
- export interface Hms {
2
- hour?: string | null
3
- minute?: string | null
4
- second?: string | null
5
- }
1
+ import type { Hms } from '../../support/Day'
6
2
 
7
- export type HmsType = 'h' | 'm' | 's'
3
+ type HmsType = 'h' | 'm' | 's'
8
4
 
9
- export const HmsMap = {
5
+ const HmsMap = {
10
6
  h: 'hour',
11
7
  m: 'minute',
12
8
  s: 'second'
@@ -1,7 +1,12 @@
1
- import type { Ymd, YmdType } from './ymd'
2
- import { YmdMap } from './ymd'
1
+ import type { Ymd } from '../../support/Day'
3
2
 
4
- export type { Ymd, YmdType, YmdMap }
3
+ type YmdType = 'y' | 'm' | 'd'
4
+
5
+ const YmdMap = {
6
+ y: 'year',
7
+ m: 'month',
8
+ d: 'date'
9
+ } as const
5
10
 
6
11
  export function requiredYmd(ymd: Ymd, required: YmdType[] = ['y', 'm', 'd']): boolean {
7
12
  return required.every((r) => ymd[YmdMap[r]] != null)
@@ -1,14 +1,9 @@
1
1
  import day from 'dayjs'
2
+ import type { Ymd } from '../../support/Day'
2
3
 
3
- export interface Ymd {
4
- year: number | null
5
- month: number | null
6
- date: number | null
7
- }
8
-
9
- export type YmdType = 'y' | 'm' | 'd'
4
+ type YmdType = 'y' | 'm' | 'd'
10
5
 
11
- export const YmdMap = {
6
+ const YmdMap = {
12
7
  y: 'year',
13
8
  m: 'month',
14
9
  d: 'date'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "2.23.0",
3
+ "version": "2.25.0",
4
4
  "packageManager": "pnpm@7.26.2",
5
5
  "description": "Vue Components for Global Brain Design System.",
6
6
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",
@@ -53,10 +53,10 @@
53
53
  "@types/body-scroll-lock": "^3.1.0",
54
54
  "@types/lodash-es": "^4.17.6",
55
55
  "@types/markdown-it": "^12.2.3",
56
- "@types/node": "^18.11.18",
56
+ "@types/node": "^18.14.6",
57
57
  "@vitejs/plugin-vue": "^4.0.0",
58
- "@vitest/coverage-c8": "^0.28.1",
59
- "@vue/test-utils": "^2.2.7",
58
+ "@vitest/coverage-c8": "^0.29.2",
59
+ "@vue/test-utils": "^2.3.0",
60
60
  "@vuelidate/core": "^2.0.0",
61
61
  "@vuelidate/validators": "^2.0.0",
62
62
  "@vueuse/core": "^9.11.1",
@@ -77,12 +77,12 @@
77
77
  "semver": "^7.3.8",
78
78
  "typescript": "^4.9.4",
79
79
  "v-calendar": "3.0.0-alpha.8",
80
- "vite": "^4.0.4",
81
- "vitepress": "1.0.0-alpha.40",
82
- "vitest": "^0.28.1",
80
+ "vite": "^4.1.4",
81
+ "vitepress": "1.0.0-alpha.50",
82
+ "vitest": "^0.29.2",
83
83
  "vue": "^3.2.45",
84
84
  "vue-router": "^4.1.6",
85
- "vue-tsc": "^1.0.24"
85
+ "vue-tsc": "^1.2.0"
86
86
  },
87
87
  "scripts": {
88
88
  "docs": "vitepress dev docs --port 4000",