@globalbrain/sefirot 4.45.0 → 4.47.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,6 +1,7 @@
1
1
  export type Rule =
2
2
  | MaxLengthRule
3
3
  | RequiredRule
4
+ | SlackChannelLinkRule
4
5
  | SlackChannelNameRule
5
6
  | BeforeRule
6
7
  | BeforeOrEqualRule
@@ -16,6 +17,10 @@ export interface RequiredRule {
16
17
  type: 'required'
17
18
  }
18
19
 
20
+ export interface SlackChannelLinkRule {
21
+ type: 'slack_channel_link'
22
+ }
23
+
19
24
  export interface SlackChannelNameRule {
20
25
  type: 'slack_channel_name'
21
26
  offset: number
@@ -3,6 +3,7 @@ import { useDebounceFn, useElementSize } from '@vueuse/core'
3
3
  import { computed, ref, watch } from 'vue'
4
4
  import SDivider from '../../../components/SDivider.vue'
5
5
  import { useQuery } from '../../../composables/Api'
6
+ import { useLang } from '../../../composables/Lang'
6
7
  import { usePower } from '../../../composables/Power'
7
8
  import { type FieldData } from '../FieldData'
8
9
  import { type LensQuery, type LensQuerySort } from '../LensQuery'
@@ -180,12 +181,15 @@ const _overrides = ref(props.overrides ?? {})
180
181
  const page = ref(1)
181
182
  const perPage = ref(100)
182
183
 
184
+ const lang = useLang()
185
+
183
186
  const { data: result, execute: refresh, loading } = useQuery(async (http) => {
184
187
  const input = {
185
188
  entity: props.entity ?? '__no_entity__',
186
189
  select: withIndexField(_select.value),
187
190
  filters: createInputFilters(queryFilter.value, _filters.value),
188
191
  sort: _sort.value.length > 0 ? _sort.value : defaultSort.value ?? [],
192
+ settings: { lang },
189
193
  page: page.value,
190
194
  perPage: perPage.value
191
195
  }
@@ -1,6 +1,15 @@
1
1
  import { type ValidationArgs, type ValidationRuleWithParams } from '@vuelidate/core'
2
2
  import { type Day, day } from '../../../support/Day'
3
- import { after, afterOrEqual, before, beforeOrEqual, maxLength, required, slackChannelName } from '../../../validation/rules'
3
+ import {
4
+ after,
5
+ afterOrEqual,
6
+ before,
7
+ beforeOrEqual,
8
+ maxLength,
9
+ required,
10
+ slackChannelLink,
11
+ slackChannelName
12
+ } from '../../../validation/rules'
4
13
  import { type Rule } from '../Rule'
5
14
 
6
15
  /**
@@ -19,6 +28,8 @@ function mapRule(rule: Rule): ValidationRuleWithParams {
19
28
  return maxLength(rule.length)
20
29
  case 'required':
21
30
  return required()
31
+ case 'slack_channel_link':
32
+ return slackChannelLink()
22
33
  case 'slack_channel_name':
23
34
  return slackChannelName({ offset: rule.offset })
24
35
  case 'before':
@@ -24,6 +24,7 @@ export { requiredIf } from './requiredIf'
24
24
  export { requiredYmd } from './requiredYmd'
25
25
  export { requiredYmdIf } from './requiredYmdIf'
26
26
  export { rule } from './rule'
27
+ export { slackChannelLink } from './slackChannelLink'
27
28
  export { slackChannelName } from './slackChannelName'
28
29
  export { url } from './url'
29
30
  export { ymd } from './ymd'
@@ -0,0 +1,15 @@
1
+ import { createRule } from '../Rule'
2
+ import { slackChannelLink as baseSlackChannelLink } from '../validators'
3
+
4
+ export const message = {
5
+ en: 'The slack channel link is invalid.',
6
+ ja: 'Slackチャンネルリンクの形式が正しくありません。'
7
+ }
8
+
9
+ export function slackChannelLink(msg?: string) {
10
+ return createRule({
11
+ message: ({ lang }) => msg ?? message[lang],
12
+ optional: true,
13
+ validation: baseSlackChannelLink
14
+ })
15
+ }
@@ -21,6 +21,7 @@ export * from './required'
21
21
  export * from './requiredHmsIf'
22
22
  export * from './requiredIf'
23
23
  export * from './requiredYmdIf'
24
+ export * from './slackChannelLink'
24
25
  export * from './slackChannelName'
25
26
  export * from './url'
26
27
  export * from './ymd'
@@ -0,0 +1,52 @@
1
+ const channelIdRE = /^[CG][A-Z0-9]{6,}$/
2
+
3
+ /**
4
+ * Checks that the value identifies a Slack channel. Accepts a channel link as
5
+ * copied from Slack (`https://<workspace>.slack.com/archives/<id>`, with or
6
+ * without a trailing message path), a browser URL
7
+ * (`https://app.slack.com/client/<team-id>/<id>`), or a raw channel ID.
8
+ * Mirrors the backend `slack_channel_link` rule definition.
9
+ */
10
+ export function slackChannelLink(value: unknown): boolean {
11
+ return typeof value === 'string' && extractChannelId(value) !== null
12
+ }
13
+
14
+ function extractChannelId(value: string): string | null {
15
+ const trimmed = value.trim()
16
+
17
+ if (channelIdRE.test(trimmed)) {
18
+ return trimmed
19
+ }
20
+
21
+ return extractChannelIdFromUrl(trimmed)
22
+ }
23
+
24
+ function extractChannelIdFromUrl(value: string): string | null {
25
+ let url: URL
26
+
27
+ try {
28
+ url = new URL(value)
29
+ } catch {
30
+ return null
31
+ }
32
+
33
+ if (url.protocol !== 'https:') {
34
+ return null
35
+ }
36
+
37
+ if (url.hostname !== 'slack.com' && !url.hostname.endsWith('.slack.com')) {
38
+ return null
39
+ }
40
+
41
+ const segments = url.pathname.split('/').filter((segment) => segment !== '')
42
+
43
+ let candidate: string | undefined
44
+
45
+ if (segments[0] === 'archives') {
46
+ candidate = segments[1]
47
+ } else if (url.hostname === 'app.slack.com' && segments[0] === 'client') {
48
+ candidate = segments[2]
49
+ }
50
+
51
+ return candidate !== undefined && channelIdRE.test(candidate) ? candidate : null
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "4.45.0",
3
+ "version": "4.47.0",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "keywords": [
6
6
  "components",