@globalbrain/sefirot 2.42.0 → 2.44.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,14 +1,28 @@
1
1
  <script setup lang="ts">
2
+ import { provide } from 'vue'
2
3
  import SGrid from './SGrid.vue'
3
4
 
4
- defineProps<{
5
+ const props = withDefaults(defineProps<{
5
6
  cols?: string | number
6
7
  gap?: string | number
7
- }>()
8
+ dir?: 'column' | 'row'
9
+ labelWidth?: string | number
10
+ divider?: boolean
11
+ }>(), {
12
+ dir: 'column',
13
+ divider: true
14
+ })
15
+
16
+ provide('sefirot-desc-label-wdith', () => props.labelWidth)
8
17
  </script>
9
18
 
10
19
  <template>
11
- <SGrid class="SDesc" :cols="cols" :gap="gap">
20
+ <SGrid
21
+ class="SDesc"
22
+ :class="[dir, { divider }]"
23
+ :cols="cols"
24
+ :gap="gap"
25
+ >
12
26
  <slot />
13
27
  </SGrid>
14
28
  </template>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, useSlots } from 'vue'
2
+ import { computed } from 'vue'
3
+ import { useSlotValue } from '../composables/Utils'
3
4
  import { type Day } from '../support/Day'
4
5
  import SDescEmpty from './SDescEmpty.vue'
5
6
 
@@ -8,13 +9,11 @@ const props = defineProps<{
8
9
  format?: string
9
10
  }>()
10
11
 
11
- const slots = useSlots()
12
+ const slotValue = useSlotValue()
12
13
 
13
14
  const _value = computed(() => {
14
- const slotValue = slots.default?.()[0].children
15
-
16
- if (typeof slotValue === 'string') {
17
- return slotValue
15
+ if (slotValue.value) {
16
+ return slotValue.value
18
17
  }
19
18
 
20
19
  if (props.value) {
@@ -35,11 +34,6 @@ const _value = computed(() => {
35
34
  </template>
36
35
 
37
36
  <style scoped lang="postcss">
38
- .SDescDay {
39
- border-bottom: 1px dashed var(--c-divider-1);
40
- padding-bottom: 7px;
41
- }
42
-
43
37
  .value {
44
38
  line-height: 24px;
45
39
  font-size: 14px;
@@ -5,11 +5,6 @@
5
5
  </template>
6
6
 
7
7
  <style scoped lang="postcss">
8
- .SDescEmpty {
9
- border-bottom: 1px dashed var(--c-divider-1);
10
- padding-bottom: 7px;
11
- }
12
-
13
8
  .value {
14
9
  line-height: 24px;
15
10
  font-size: 14px;
@@ -1,9 +1,17 @@
1
1
  <script setup lang="ts">
2
+ import { computed, inject } from 'vue'
2
3
  import SGridItem from './SGridItem.vue'
3
4
 
4
5
  defineProps<{
5
6
  span?: string | number
6
7
  }>()
8
+
9
+ const labelWidthProp = inject<() => string | number | undefined>('sefirot-desc-label-wdith')
10
+
11
+ const labelWidth = computed(() => {
12
+ const w = labelWidthProp?.()
13
+ return w ? `${w}px` : '1fr'
14
+ })
7
15
  </script>
8
16
 
9
17
  <template>
@@ -11,3 +19,18 @@ defineProps<{
11
19
  <slot />
12
20
  </SGridItem>
13
21
  </template>
22
+
23
+ <style scoped lang="postcss">
24
+ .SDescItem {
25
+ display: grid;
26
+ }
27
+
28
+ .SDesc.row > .SDescItem {
29
+ grid-template-columns: var(--desc-label-width, v-bind(labelWidth)) 1fr;
30
+ }
31
+
32
+ .SDesc.divider > .SDescItem {
33
+ border-bottom: 1px dashed var(--c-divider-1);
34
+ padding-bottom: 7px;
35
+ }
36
+ </style>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, useSlots } from 'vue'
2
+ import { computed } from 'vue'
3
+ import { useSlotValue } from '../composables/Utils'
3
4
  import SDescEmpty from './SDescEmpty.vue'
4
5
  import SLink from './SLink.vue'
5
6
 
@@ -8,27 +9,23 @@ const props = defineProps<{
8
9
  href?: string
9
10
  }>()
10
11
 
11
- const slots = useSlots()
12
+ const slotValue = useSlotValue()
12
13
 
13
14
  const link = computed(() => {
14
15
  if (props.href) {
15
16
  return props.href
16
17
  }
17
18
 
18
- const slotValue = slots.default?.()[0].children
19
-
20
- if (typeof slotValue === 'string') {
21
- return slotValue
22
- }
23
-
24
- return props.value
19
+ return slotValue.value
20
+ ? slotValue.value
21
+ : props.value
25
22
  })
26
23
  </script>
27
24
 
28
25
  <template>
29
- <div v-if="$slots.default || value" class="SDescLink">
26
+ <div v-if="slotValue || value" class="SDescLink">
30
27
  <SLink class="value" :href="link">
31
- <slot v-if="$slots.default" />
28
+ <slot v-if="slotValue" />
32
29
  <template v-else>{{ value }}</template>
33
30
  </SLink>
34
31
  </div>
@@ -36,11 +33,6 @@ const link = computed(() => {
36
33
  </template>
37
34
 
38
35
  <style scoped lang="postcss">
39
- .SDescLink {
40
- border-bottom: 1px dashed var(--c-divider-1);
41
- padding-bottom: 7px;
42
- }
43
-
44
36
  .value {
45
37
  display: block;
46
38
  line-height: 24px;
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, useSlots } from 'vue'
2
+ import { computed } from 'vue'
3
+ import { useSlotValue } from '../composables/Utils'
3
4
  import { format } from '../support/Num'
4
5
  import SDescEmpty from './SDescEmpty.vue'
5
6
 
@@ -8,12 +9,12 @@ const props = defineProps<{
8
9
  separator?: boolean
9
10
  }>()
10
11
 
11
- const slots = useSlots()
12
+ const slotValue = useSlotValue()
12
13
 
13
14
  const _value = computed(() => {
14
- const slotValue = slots.default?.()[0].children
15
+ const sv = slotValue.value
15
16
 
16
- const v = (typeof slotValue === 'string') ? Number(slotValue) : props.value
17
+ const v = sv ? Number(sv) : props.value
17
18
 
18
19
  if (v == null) {
19
20
  return null
@@ -33,11 +34,6 @@ const _value = computed(() => {
33
34
  </template>
34
35
 
35
36
  <style scoped lang="postcss">
36
- .SDescNumber {
37
- border-bottom: 1px dashed var(--c-divider-1);
38
- padding-bottom: 7px;
39
- }
40
-
41
37
  .value {
42
38
  line-height: 24px;
43
39
  font-size: 14px;
@@ -32,11 +32,6 @@ const pills = computed(() => {
32
32
  </template>
33
33
 
34
34
  <style scoped lang="postcss">
35
- .SDescPill {
36
- border-bottom: 1px dashed var(--c-divider-1);
37
- padding-bottom: 7px;
38
- }
39
-
40
35
  .value {
41
36
  display: flex;
42
37
  gap: 4px 6px;
@@ -22,11 +22,6 @@ defineProps<{
22
22
  </template>
23
23
 
24
24
  <style scoped lang="postcss">
25
- .SDescState {
26
- border-bottom: 1px dashed var(--c-divider-1);
27
- padding-bottom: 7px;
28
- }
29
-
30
25
  .value {
31
26
  display: flex;
32
27
  align-items: center;
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
+ import { useHasSlotContent } from '../composables/Utils'
3
4
  import SDescEmpty from './SDescEmpty.vue'
4
5
 
5
6
  const props = defineProps<{
@@ -13,13 +14,15 @@ const classes = computed(() => [
13
14
  { 'pre-wrap': props.preWrap }
14
15
  ])
15
16
 
17
+ const hasSlot = useHasSlotContent()
18
+
16
19
  const lineClamp = computed(() => props.lineClamp ?? 'none')
17
20
  </script>
18
21
 
19
22
  <template>
20
- <div v-if="$slots.default || value" class="SDescText" :class="classes">
23
+ <div v-if="hasSlot || value" class="SDescText" :class="classes">
21
24
  <div class="value">
22
- <slot v-if="$slots.default" />
25
+ <slot v-if="hasSlot" />
23
26
  <template v-else>{{ value }}</template>
24
27
  </div>
25
28
  </div>
@@ -27,11 +30,6 @@ const lineClamp = computed(() => props.lineClamp ?? 'none')
27
30
  </template>
28
31
 
29
32
  <style scoped lang="postcss">
30
- .SDescText {
31
- border-bottom: 1px dashed var(--c-divider-1);
32
- padding-bottom: 7px;
33
- }
34
-
35
33
  .value {
36
34
  line-height: 24px;
37
35
  font-size: 14px;
@@ -50,6 +48,10 @@ const lineClamp = computed(() => props.lineClamp ?? 'none')
50
48
  }
51
49
  }
52
50
 
51
+ .value :deep(p + p) {
52
+ margin-top: 12px;
53
+ }
54
+
53
55
  .value :deep(a) {
54
56
  color: var(--c-info-text);
55
57
  transition: color 0.25s;
@@ -19,6 +19,7 @@ const props = withDefaults(defineProps<{
19
19
  checkText?: string
20
20
  checkColor?: Color
21
21
  text?: string
22
+ disabled?: boolean
22
23
  value?: boolean
23
24
  modelValue?: boolean
24
25
  validation?: Validatable
@@ -33,6 +34,11 @@ const emit = defineEmits<{
33
34
  (e: 'change', value: boolean): void
34
35
  }>()
35
36
 
37
+ const classes = computed(() => [
38
+ props.size ?? 'small',
39
+ { disabled: props.disabled }
40
+ ])
41
+
36
42
  const _value = computed(() => {
37
43
  return props.modelValue !== undefined
38
44
  ? props.modelValue
@@ -40,15 +46,17 @@ const _value = computed(() => {
40
46
  })
41
47
 
42
48
  function onClick() {
43
- emit('update:model-value', !_value.value)
44
- emit('change', !_value.value)
49
+ if (!props.disabled) {
50
+ emit('update:model-value', !_value.value)
51
+ emit('change', !_value.value)
52
+ }
45
53
  }
46
54
  </script>
47
55
 
48
56
  <template>
49
57
  <SInputBase
50
58
  class="SInputCheckbox"
51
- :class="[size ?? 'small']"
59
+ :class="classes"
52
60
  :label="label"
53
61
  :note="note"
54
62
  :info="info"
@@ -59,7 +67,13 @@ function onClick() {
59
67
  :validation="validation"
60
68
  >
61
69
  <div class="container">
62
- <div class="input" :class="{ on: _value }" role="button" @click="onClick">
70
+ <div
71
+ class="input"
72
+ :class="{ on: _value }"
73
+ role="button"
74
+ @click="onClick"
75
+ :aria-disabled="disabled"
76
+ >
63
77
  <div class="box">
64
78
  <div class="check">
65
79
  <SIcon :icon="IconCheck" class="check-icon" />
@@ -139,4 +153,16 @@ function onClick() {
139
153
  font-size: 14px;
140
154
  font-weight: 400;
141
155
  }
156
+
157
+ .SInputCheckbox.disabled {
158
+ .box {
159
+ border-color: var(--input-disabled-border-color);
160
+ background-color: var(--input-disabled-bg-color);
161
+
162
+ &:hover { border-color: var(--input-disabled-border-color); }
163
+ &:focus:not(:focus-visible) { border-color: var(--input-disabled-border-color); }
164
+ }
165
+
166
+ .input { cursor: not-allowed; }
167
+ }
142
168
  </style>
@@ -13,6 +13,7 @@ export type Value = string | number | boolean
13
13
  export interface Option {
14
14
  label: string
15
15
  value: Value
16
+ disabled?: boolean
16
17
  }
17
18
 
18
19
  const props = withDefaults(defineProps<{
@@ -27,6 +28,7 @@ const props = withDefaults(defineProps<{
27
28
  checkColor?: Color
28
29
  options: Option[]
29
30
  nullable?: boolean
31
+ disabled?: boolean
30
32
  value?: Value[]
31
33
  modelValue?: Value[]
32
34
  validation?: Validatable
@@ -82,6 +84,7 @@ function handleChange(value: Value): void {
82
84
  <div v-for="option in options" :key="String(option.value)" class="col">
83
85
  <SInputCheckbox
84
86
  :text="option.label"
87
+ :disabled="option.disabled ?? disabled"
85
88
  :model-value="isChecked(option.value)"
86
89
  @update:model-value="handleChange(option.value)"
87
90
  />
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { type IconifyIcon } from '@iconify/vue/dist/offline'
3
- import { type DefineComponent } from 'vue'
3
+ import { type DefineComponent, computed } from 'vue'
4
4
  import { type Validatable } from '../composables/Validation'
5
5
  import SInputBase from './SInputBase.vue'
6
6
 
@@ -18,6 +18,7 @@ const props = defineProps<{
18
18
  checkText?: string
19
19
  checkColor?: Color
20
20
  text: string
21
+ disabled?: boolean
21
22
  modelValue: boolean
22
23
  validation?: Validatable
23
24
  hideError?: boolean
@@ -28,16 +29,23 @@ const emit = defineEmits<{
28
29
  (e: 'change', value: boolean): void
29
30
  }>()
30
31
 
32
+ const classes = computed(() => [
33
+ props.size ?? 'small',
34
+ { disabled: props.disabled }
35
+ ])
36
+
31
37
  function onClick() {
32
- emit('update:model-value', !props.modelValue)
33
- emit('change', !props.modelValue)
38
+ if (!props.disabled) {
39
+ emit('update:model-value', !props.modelValue)
40
+ emit('change', !props.modelValue)
41
+ }
34
42
  }
35
43
  </script>
36
44
 
37
45
  <template>
38
46
  <SInputBase
39
47
  class="SInputRadio"
40
- :class="[size ?? 'small']"
48
+ :class="classes"
41
49
  :label="label"
42
50
  :note="note"
43
51
  :info="info"
@@ -49,7 +57,13 @@ function onClick() {
49
57
  :hide-error="hideError"
50
58
  >
51
59
  <div class="container">
52
- <div class="input" :class="{ on: props.modelValue }" role="button" @click="onClick">
60
+ <div
61
+ class="input"
62
+ :class="{ on: props.modelValue }"
63
+ role="button"
64
+ @click="onClick"
65
+ :aria-disabled="disabled"
66
+ >
53
67
  <div class="box">
54
68
  <div class="check" />
55
69
  </div>
@@ -125,4 +139,16 @@ function onClick() {
125
139
  font-size: 14px;
126
140
  font-weight: 400;
127
141
  }
142
+
143
+ .SInputRadio.disabled {
144
+ .box {
145
+ border-color: var(--input-disabled-border-color);
146
+ background-color: var(--input-disabled-bg-color);
147
+
148
+ &:hover { border-color: var(--input-disabled-border-color); }
149
+ &:focus:not(:focus-visible) { border-color: var(--input-disabled-border-color); }
150
+ }
151
+
152
+ .input { cursor: not-allowed; }
153
+ }
128
154
  </style>
@@ -12,6 +12,7 @@ export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'dange
12
12
  export interface Option {
13
13
  label: string
14
14
  value: string | number | boolean
15
+ disabled?: boolean
15
16
  }
16
17
 
17
18
  const props = withDefaults(defineProps<{
@@ -26,6 +27,7 @@ const props = withDefaults(defineProps<{
26
27
  checkColor?: Color
27
28
  options: Option[]
28
29
  nullable?: boolean
30
+ disabled?: boolean
29
31
  value?: string | number | boolean | null
30
32
  modelValue?: string | number | boolean | null
31
33
  validation?: Validatable
@@ -92,6 +94,7 @@ function onChange(value: string | number | boolean) {
92
94
  <div v-for="(option, index) in options" :key="index" class="col">
93
95
  <SInputRadio
94
96
  :text="option.label"
97
+ :disabled="option.disabled ?? disabled"
95
98
  :model-value="isChecked(option.value)"
96
99
  @update:model-value="onUpdate(option.value)"
97
100
  @change="onChange(option.value)"
@@ -277,6 +277,9 @@ function createRequiredTouched(): boolean[] {
277
277
  .container {
278
278
  background-color: var(--input-disabled-bg-color);
279
279
  }
280
+
281
+ .container:hover { border-color: var(--input-border-color); }
282
+ .container.focus { border-color: var(--input-border-color); }
280
283
  }
281
284
 
282
285
  .SInputYMD.has-error {
@@ -1,5 +1,6 @@
1
1
  import { type MaybeRefOrGetter, resolveUnref } from '@vueuse/core'
2
- import { type ComputedRef, computed } from 'vue'
2
+ import { type ComputedRef, computed, useSlots } from 'vue'
3
+ import { isArray, isString } from '../support/Utils'
3
4
 
4
5
  export type WhenCondition<T> = MaybeRefOrGetter<T>
5
6
 
@@ -33,3 +34,31 @@ export function computedArray<T = any>(fn: (arr: T[]) => void): ComputedRef<T[]>
33
34
  return arr
34
35
  })
35
36
  }
37
+
38
+ /**
39
+ * Checks whether the slot has a non empty value.
40
+ */
41
+ export function useHasSlotContent(name = 'default'): ComputedRef<boolean> {
42
+ const slots = useSlots()
43
+
44
+ return computed(() => {
45
+ return !!slots[name]?.().some((s) => {
46
+ return isArray(s.children) ? true : !!(s.children as string).trim()
47
+ })
48
+ })
49
+ }
50
+
51
+ /**
52
+ * Get the slot value. If the slot contains child nodes, it will get ignored
53
+ * and treated as if it was empty. This composable is useful to get the plain
54
+ * text out of the slot content.
55
+ */
56
+ export function useSlotValue(name = 'default'): ComputedRef<string | null> {
57
+ const slots = useSlots()
58
+
59
+ return computed(() => {
60
+ const c = slots[name]?.()[0]?.children
61
+ const v = isString(c) ? c.trim() : null
62
+ return v !== '' ? v : null
63
+ })
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "2.42.0",
3
+ "version": "2.44.0",
4
4
  "packageManager": "pnpm@8.6.2",
5
5
  "description": "Vue Components for Global Brain Design System.",
6
6
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",