@globalbrain/sefirot 2.22.0 → 2.24.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,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { IconifyIcon } from '@iconify/vue/dist/offline'
3
- import IconCheck from '@iconify-icons/ph/check'
4
- import type { DefineComponent, PropType } from 'vue'
3
+ import IconCheck from '@iconify-icons/ph/check-bold'
4
+ import { computed } from 'vue'
5
5
  import type { Validatable } from '../composables/Validation'
6
6
  import SIcon from './SIcon.vue'
7
7
  import SInputBase from './SInputBase.vue'
@@ -9,31 +9,46 @@ import SInputBase from './SInputBase.vue'
9
9
  export type Size = 'mini' | 'small' | 'medium'
10
10
  export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
11
11
 
12
- const props = defineProps({
13
- size: { type: String as PropType<Size>, default: 'small' },
14
- label: { type: String, default: null },
15
- info: { type: String, default: null },
16
- note: { type: String, default: null },
17
- help: { type: String, default: null },
18
- checkIcon: { type: Object as PropType<IconifyIcon | DefineComponent>, default: null },
19
- checkText: { type: String, default: null },
20
- checkColor: { type: String as PropType<Color>, default: null },
21
- text: { type: String, required: true },
22
- modelValue: { type: Boolean, required: true },
23
- validation: { type: Object as PropType<Validatable>, default: null }
12
+ const props = withDefaults(defineProps<{
13
+ size?: Size
14
+ label?: string
15
+ info?: string
16
+ note?: string
17
+ help?: string
18
+ checkIcon?: IconifyIcon
19
+ checkText?: string
20
+ checkColor?: Color
21
+ text?: string
22
+ value?: boolean
23
+ modelValue?: boolean
24
+ validation?: Validatable
25
+ hideError?: boolean
26
+ }>(), {
27
+ value: undefined,
28
+ modelValue: undefined
24
29
  })
25
30
 
26
- const emit = defineEmits(['update:modelValue'])
31
+ const emit = defineEmits<{
32
+ (e: 'update:model-value', value: boolean): void
33
+ (e: 'change', value: boolean): void
34
+ }>()
27
35
 
28
- function emitChange() {
29
- emit('update:modelValue', !props.modelValue)
36
+ const _value = computed(() => {
37
+ return props.modelValue !== undefined
38
+ ? props.modelValue
39
+ : props.value !== undefined ? props.value : false
40
+ })
41
+
42
+ function onClick() {
43
+ emit('update:model-value', !_value.value)
44
+ emit('change', !_value.value)
30
45
  }
31
46
  </script>
32
47
 
33
48
  <template>
34
49
  <SInputBase
35
50
  class="SInputCheckbox"
36
- :class="[size]"
51
+ :class="[size ?? 'small']"
37
52
  :label="label"
38
53
  :note="note"
39
54
  :info="info"
@@ -44,21 +59,21 @@ function emitChange() {
44
59
  :validation="validation"
45
60
  >
46
61
  <div class="container">
47
- <div class="input" :class="{ on: modelValue }" role="button" @click="emitChange">
62
+ <div class="input" :class="{ on: _value }" role="button" @click="onClick">
48
63
  <div class="box">
49
64
  <div class="check">
50
65
  <SIcon :icon="IconCheck" class="check-icon" />
51
66
  </div>
52
67
  </div>
53
68
 
54
- <p class="text">{{ text }}</p>
69
+ <p v-if="text" class="text">{{ text }}</p>
55
70
  </div>
56
71
  </div>
57
72
  <template v-if="$slots.info" #info><slot name="info" /></template>
58
73
  </SInputBase>
59
74
  </template>
60
75
 
61
- <style lang="postcss" scoped>
76
+ <style scoped lang="postcss">
62
77
  .container {
63
78
  display: flex;
64
79
  }
@@ -68,19 +83,19 @@ function emitChange() {
68
83
  display: flex;
69
84
  align-items: center;
70
85
  height: 32px;
86
+ cursor: pointer;
71
87
 
72
88
  &:hover {
73
89
  .box {
74
- border-color: var(--c-black);
90
+ border-color: var(--c-info-light);
75
91
  }
76
92
  }
77
93
  }
78
94
 
79
95
  .input.on {
80
96
  .box {
81
- border-color: var(--c-black);
82
- background-color: var(--c-black);
83
- box-shadow: var(--shadow-depth-3);
97
+ border-color: var(--c-info-light);
98
+ background-color: var(--c-info-light);
84
99
  }
85
100
 
86
101
  .check {
@@ -91,11 +106,12 @@ function emitChange() {
91
106
 
92
107
  .box {
93
108
  position: relative;
94
- border: 2px solid var(--c-text-3);
95
- border-radius: 2px;
96
- width: 18px;
97
- height: 18px;
98
- transition: border-color .25s, background-color .25s, box-shadow .25s;
109
+ border: 1px solid var(--c-divider-1);
110
+ border-radius: 4px;
111
+ width: 16px;
112
+ height: 16px;
113
+ background-color: var(--input-bg-color);
114
+ transition: border-color 0.25s, background-color 0.25s;
99
115
  }
100
116
 
101
117
  .check {
@@ -118,9 +134,9 @@ function emitChange() {
118
134
 
119
135
  .text {
120
136
  margin: 0;
121
- padding-left: 12px;
137
+ padding-left: 10px;
122
138
  line-height: 20px;
123
139
  font-size: 14px;
124
- font-weight: 500;
140
+ font-weight: 400;
125
141
  }
126
142
  </style>
@@ -1,50 +1,73 @@
1
1
  <script setup lang="ts">
2
2
  import type { IconifyIcon } from '@iconify/vue/dist/offline'
3
- import type { DefineComponent, PropType } from 'vue'
3
+ import { computed } from 'vue'
4
+ import type { Validatable } from '../composables/Validation'
4
5
  import SInputBase from './SInputBase.vue'
5
6
  import SInputCheckbox from './SInputCheckbox.vue'
6
7
 
7
8
  export type Size = 'mini' | 'small' | 'medium'
8
9
  export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
9
10
 
10
- interface CheckboxOption {
11
+ export type Value = string | number | boolean
12
+
13
+ export interface Option {
11
14
  label: string
12
- value: any
15
+ value: Value
13
16
  }
14
17
 
15
- const props = defineProps({
16
- size: { type: String as PropType<Size>, default: 'small' },
17
- name: { type: String, default: null },
18
- label: { type: String, default: null },
19
- info: { type: String, default: null },
20
- note: { type: String, default: null },
21
- help: { type: String, default: null },
22
- checkIcon: { type: Object as PropType<IconifyIcon | DefineComponent>, default: null },
23
- checkText: { type: String, default: null },
24
- checkColor: { type: String as PropType<Color>, default: null },
25
- options: { type: Array as PropType<CheckboxOption[]>, required: true },
26
- modelValue: { type: Array, required: true }
18
+ const props = withDefaults(defineProps<{
19
+ size?: Size
20
+ name?: string
21
+ label?: string
22
+ info?: string
23
+ note?: string
24
+ help?: string
25
+ checkIcon?: IconifyIcon
26
+ checkText?: string
27
+ checkColor?: Color
28
+ options: Option[]
29
+ nullable?: boolean
30
+ value?: Value[]
31
+ modelValue?: Value[]
32
+ validation?: Validatable
33
+ hideError?: boolean
34
+ }>(), {
35
+ nullable: true
27
36
  })
28
37
 
29
- const emit = defineEmits(['update:modelValue'])
38
+ const emit = defineEmits<{
39
+ (e: 'update:model-value', value: Value[]): void
40
+ (e: 'change', value: Value[]): void
41
+ }>()
42
+
43
+ const _value = computed(() => {
44
+ return props.modelValue !== undefined
45
+ ? props.modelValue
46
+ : props.value !== undefined ? props.value : []
47
+ })
30
48
 
31
- function isChecked(value: unknown): boolean {
32
- return props.modelValue.includes(value)
49
+ function isChecked(value: Value): boolean {
50
+ return _value.value.includes(value)
33
51
  }
34
52
 
35
- function handleChange(value: unknown): void {
36
- const distinct = props.modelValue
53
+ function handleChange(value: Value): void {
54
+ const distinct = _value.value
37
55
  .filter((v) => v !== value)
38
- .concat(props.modelValue.includes(value) ? [] : [value])
56
+ .concat(_value.value.includes(value) ? [] : [value])
39
57
 
40
- emit('update:modelValue', distinct)
58
+ if (distinct.length === 0 && !props.nullable) {
59
+ return
60
+ }
61
+
62
+ emit('update:model-value', distinct)
63
+ emit('change', distinct)
41
64
  }
42
65
  </script>
43
66
 
44
67
  <template>
45
68
  <SInputBase
46
69
  class="SInputCheckboxes"
47
- :class="[size]"
70
+ :class="[size ?? 'small']"
48
71
  :name="name"
49
72
  :label="label"
50
73
  :note="note"
@@ -56,7 +79,7 @@ function handleChange(value: unknown): void {
56
79
  >
57
80
  <div class="container">
58
81
  <div class="row">
59
- <div v-for="option in options" :key="option.value" class="col">
82
+ <div v-for="option in options" :key="String(option.value)" class="col">
60
83
  <SInputCheckbox
61
84
  :text="option.label"
62
85
  :model-value="isChecked(option.value)"
@@ -68,17 +91,3 @@ function handleChange(value: unknown): void {
68
91
  <template v-if="$slots.info" #info><slot name="info" /></template>
69
92
  </SInputBase>
70
93
  </template>
71
-
72
- <style lang="postcss" scoped>
73
- .container {
74
- display: flex;
75
- }
76
-
77
- .row {
78
- margin: -2px -8px;
79
- }
80
-
81
- .col {
82
- padding: 2px 8px;
83
- }
84
- </style>
@@ -111,7 +111,7 @@ function onChange(e: Event) {
111
111
  .placeholder {
112
112
  line-height: 30px;
113
113
  font-size: var(--input-font-size, var(--input-mini-font-size));
114
- font-weight: 500;
114
+ font-weight: 400;
115
115
  }
116
116
  }
117
117
 
@@ -123,7 +123,7 @@ function onChange(e: Event) {
123
123
  .button {
124
124
  padding: 0 8px;
125
125
  line-height: 26px;
126
- font-size: 14px;
126
+ font-size: 13px;
127
127
  font-weight: 500;
128
128
  }
129
129
 
@@ -133,10 +133,6 @@ function onChange(e: Event) {
133
133
  font-size: var(--input-font-size, var(--input-small-font-size));
134
134
  font-weight: 400;
135
135
  }
136
-
137
- .placeholder {
138
- font-weight: 500;
139
- }
140
136
  }
141
137
 
142
138
  .SInputFile.medium {
@@ -157,10 +153,6 @@ function onChange(e: Event) {
157
153
  font-size: var(--input-font-size, var(--input-medium-font-size));
158
154
  font-weight: 400;
159
155
  }
160
-
161
- .placeholder {
162
- font-weight: 500;
163
- }
164
156
  }
165
157
 
166
158
  .SInputFile.has-error {
@@ -112,7 +112,7 @@ function onClick() {
112
112
  align-items: center;
113
113
  border-radius: 50%;
114
114
  width: 100%;
115
- background-color: var(--c-info-bg);
115
+ background-color: var(--c-info-light);
116
116
  opacity: 0;
117
117
  transform: scale(0);
118
118
  transition: opacity 0.25s, transform 0.1s;
@@ -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(
@@ -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.22.0",
3
+ "version": "2.24.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",