@globalbrain/sefirot 3.36.0 → 3.38.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.
@@ -44,6 +44,8 @@ const initial = computed(() => props.name?.charAt(0).toUpperCase())
44
44
 
45
45
  .img {
46
46
  object-fit: cover;
47
+ height: 100%;
48
+ width: 100%;
47
49
  }
48
50
 
49
51
  .initial {
@@ -163,7 +163,11 @@ function onClick() {
163
163
  }
164
164
 
165
165
  .SInputCheckbox.disabled {
166
- .box {
166
+ .input {
167
+ cursor: not-allowed;
168
+ }
169
+
170
+ .input .box {
167
171
  border-color: var(--input-disabled-border-color);
168
172
  background-color: var(--input-disabled-bg-color);
169
173
 
@@ -171,6 +175,13 @@ function onClick() {
171
175
  &:focus:not(:focus-visible) { border-color: var(--input-disabled-border-color); }
172
176
  }
173
177
 
174
- .input { cursor: not-allowed; }
178
+ .input.on .box {
179
+ border-color: var(--c-border-info-1);
180
+ background-color: var(--c-blue-8);
181
+ }
182
+
183
+ .check-icon {
184
+ color: var(--c-white-a3);
185
+ }
175
186
  }
176
187
  </style>
@@ -141,7 +141,11 @@ function onClick() {
141
141
  }
142
142
 
143
143
  .SInputRadio.disabled {
144
- .box {
144
+ .input {
145
+ cursor: not-allowed;
146
+ }
147
+
148
+ .input .box {
145
149
  border-color: var(--input-disabled-border-color);
146
150
  background-color: var(--input-disabled-bg-color);
147
151
 
@@ -149,6 +153,12 @@ function onClick() {
149
153
  &:focus:not(:focus-visible) { border-color: var(--input-disabled-border-color); }
150
154
  }
151
155
 
152
- .input { cursor: not-allowed; }
156
+ .input.on .box {
157
+ border-color: var(--c-border-info-1);
158
+ }
159
+
160
+ .check {
161
+ background-color: var(--c-blue-9);
162
+ }
153
163
  }
154
164
  </style>
@@ -136,19 +136,17 @@ const indexes = computed(() => {
136
136
  return records.map((record, i) => indexField ? record[indexField] : i)
137
137
  })
138
138
 
139
- const selectedIndexes = reactive(new Set())
139
+ const selectedIndexes = reactive(new Set(Array.isArray(props.selected) ? props.selected : []))
140
140
 
141
141
  const control = computed({
142
142
  get() {
143
- const selected = indexes.value.filter((index) => {
144
- return selectedIndexes.has(index)
145
- })
146
-
147
- updateSelected(selected)
143
+ if (Array.isArray(props.selected)) {
144
+ return props.selected.length === indexes.value.length
145
+ ? true
146
+ : props.selected.length ? 'indeterminate' : false
147
+ }
148
148
 
149
- return selected.length === indexes.value.length
150
- ? true
151
- : selected.length ? 'indeterminate' : false
149
+ return 'indeterminate' // doesn't matter
152
150
  },
153
151
 
154
152
  set(newValue) {
@@ -170,6 +168,12 @@ watch(indexes, (newValue, oldValue) => {
170
168
  }
171
169
  })
172
170
 
171
+ watch(selectedIndexes, (newValue) => {
172
+ if (Array.isArray(props.selected)) {
173
+ updateSelected(Array.from(newValue))
174
+ }
175
+ })
176
+
173
177
  const virtualizerOptions = computed(() => ({
174
178
  count: recordsWithSummary.value.length,
175
179
  getScrollElement: () => body.value,
@@ -390,7 +394,12 @@ function updateSelected(selected: any) {
390
394
  @resize="(value) => updateColWidth(key, value, true)"
391
395
  >
392
396
  <SInputCheckbox
393
- v-if="Array.isArray(selected) && key === '__select' && unref(options.records)?.length"
397
+ v-if="
398
+ Array.isArray(selected)
399
+ && key === '__select'
400
+ && unref(options.records)?.length
401
+ && options.disableSelection == null
402
+ "
394
403
  v-model="control"
395
404
  />
396
405
  </STableColumn>
@@ -450,11 +459,13 @@ function updateSelected(selected: any) {
450
459
  v-if="Array.isArray(selected)"
451
460
  :model-value="selectedIndexes.has(indexes[index])"
452
461
  @update:model-value="c => selectedIndexes[c ? 'add' : 'delete'](indexes[index])"
462
+ :disabled="options.disableSelection?.(recordsWithSummary[index]) === true"
453
463
  />
454
464
  <SInputRadio
455
465
  v-else
456
466
  :model-value="selected === indexes[index]"
457
467
  @update:model-value="c => updateSelected(c ? indexes[index] : null)"
468
+ :disabled="options.disableSelection?.(recordsWithSummary[index]) === true"
458
469
  />
459
470
  </template>
460
471
  </STableCell>
@@ -131,6 +131,10 @@ const computedCell = computed<TableCell | undefined>(() =>
131
131
  transition: background-color 0.1s;
132
132
  overflow: hidden;
133
133
 
134
+ :where(.row:has(.input.on)) & {
135
+ background-color: var(--c-bg-elv-4);
136
+ }
137
+
134
138
  .row:hover & {
135
139
  background-color: var(--c-bg-elv-4);
136
140
  }
@@ -0,0 +1,26 @@
1
+ import { type ComputedRef, computed } from 'vue'
2
+
3
+ export type Policy<Code extends string | undefined = undefined> = ComputedRef<PolicyResponse<Code>>
4
+
5
+ export interface PolicyResponse<Code extends string | undefined = undefined> {
6
+ ok: boolean | null
7
+ code?: Code
8
+ message?: string
9
+ is(code: Code): boolean
10
+ }
11
+
12
+ export interface PolicyHelpers<Code extends string | undefined = undefined> {
13
+ allow: (code?: Code, message?: string) => PolicyResponse<Code>
14
+ deny: (code?: Code, message?: string) => PolicyResponse<Code>
15
+ pending: (code?: Code, message?: string) => PolicyResponse<Code>
16
+ }
17
+
18
+ export function usePolicy<Code extends string | undefined = undefined>(
19
+ fn: (helpers: PolicyHelpers<Code>) => PolicyResponse<Code>
20
+ ): Policy<Code> {
21
+ return computed(() => fn({
22
+ allow: (code, message) => ({ ok: true, code, message, is: (c) => c === code }),
23
+ deny: (code, message) => ({ ok: false, code, message, is: (c) => c === code }),
24
+ pending: (code, message) => ({ ok: null, code, message, is: (c) => c === code })
25
+ }))
26
+ }
@@ -16,10 +16,11 @@ export interface Table<
16
16
  loading?: MaybeRef<boolean | undefined>
17
17
  rowSize?: MaybeRef<number | undefined>
18
18
  borderless?: MaybeRef<boolean>
19
+ disableSelection?: (record: R) => boolean
19
20
 
20
21
  /**
21
22
  * @deprecated Use `<SControl>` to add equivalent features.
22
- */
23
+ */
23
24
  actions?: MaybeRef<TableHeaderAction[]>
24
25
  menu?: MaybeRef<TableMenu[] | TableMenu[][]>
25
26
  header?: MaybeRef<boolean | undefined>
@@ -1,4 +1,9 @@
1
- import { type Validation, type ValidationArgs, useVuelidate } from '@vuelidate/core'
1
+ import {
2
+ type Validation,
3
+ type ValidationArgs,
4
+ type GlobalConfig as VuelidateConfig,
5
+ useVuelidate
6
+ } from '@vuelidate/core'
2
7
  import { type MaybeRefOrGetter, type Ref, computed, toValue } from 'vue'
3
8
  import { type Snackbar, useSnackbars } from '../stores/Snackbars'
4
9
  import { useTrans } from './Lang'
@@ -10,6 +15,7 @@ export interface V<
10
15
  validation: Ref<Validation<Rules, Data>>
11
16
  validate(): Promise<boolean>
12
17
  validateAndNotify(message?: Snackbar): Promise<boolean>
18
+ notify(message?: Snackbar): void
13
19
  reset(): void
14
20
  }
15
21
 
@@ -24,24 +30,24 @@ export interface ValidatableError {
24
30
  readonly $message: string | Ref<string>
25
31
  }
26
32
 
33
+ export interface VNotification {
34
+ notify(message?: Snackbar): void
35
+ }
36
+
27
37
  export function useV<
28
38
  Data extends { [key in keyof Rules]: any },
29
39
  Rules extends ValidationArgs = ValidationArgs
30
40
  >(
31
41
  data?: MaybeRefOrGetter<Data>,
32
- rules?: MaybeRefOrGetter<Rules>
42
+ rules?: MaybeRefOrGetter<Rules>,
43
+ config?: VuelidateConfig
33
44
  ): V<Data, Rules> {
34
- const { t } = useTrans({
35
- en: { notify: 'Form contains errors. Please correct them and try again.' },
36
- ja: { notify: 'フォームにエラーがあります。内容を確認し、再度お試しください。' }
37
- })
38
-
39
- const snackbars = useSnackbars()
45
+ const { notify } = useVNotification()
40
46
 
41
47
  const d = computed(() => toValue(data) ?? {})
42
48
  const r = computed(() => toValue(rules) ?? {})
43
49
 
44
- const validation = useVuelidate(r, d) as any
50
+ const validation = useVuelidate(r, d, config) as any
45
51
 
46
52
  function reset(): void {
47
53
  validation.value.$reset()
@@ -55,10 +61,7 @@ export function useV<
55
61
  const valid = await validate()
56
62
 
57
63
  if (!valid) {
58
- snackbars.push(message ?? {
59
- mode: 'danger',
60
- text: t.notify
61
- })
64
+ notify(message)
62
65
  }
63
66
 
64
67
  return valid
@@ -68,6 +71,27 @@ export function useV<
68
71
  validation,
69
72
  validate,
70
73
  validateAndNotify,
74
+ notify,
71
75
  reset
72
76
  }
73
77
  }
78
+
79
+ export function useVNotification(): VNotification {
80
+ const { t } = useTrans({
81
+ en: { notify: 'Form contains errors. Please correct them and try again.' },
82
+ ja: { notify: 'フォームにエラーがあります。内容を確認し、再度お試しください。' }
83
+ })
84
+
85
+ const snackbars = useSnackbars()
86
+
87
+ function notify(message?: Snackbar): void {
88
+ snackbars.push(message ?? {
89
+ mode: 'danger',
90
+ text: t.notify
91
+ })
92
+ }
93
+
94
+ return {
95
+ notify
96
+ }
97
+ }
@@ -12,5 +12,5 @@ export function slackChannelName(value: unknown, options: SlackChannelNameOption
12
12
  const { offset = 0 } = options
13
13
  const maxLength = /* Slack channel name max length */ 80 - offset
14
14
 
15
- return new RegExp(`^[\\p{L}\\p{M}\\p{N}_-]{1,${maxLength}}$`, 'u').test(value)
15
+ return new RegExp(`^[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}\\p{N}_-]{1,${maxLength}}$`, 'u').test(value)
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "3.36.0",
3
+ "version": "3.38.0",
4
4
  "packageManager": "pnpm@8.15.4",
5
5
  "description": "Vue Components for Global Brain Design System.",
6
6
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",