@globalbrain/sefirot 3.48.0 → 3.50.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,8 +1,12 @@
1
1
  <script setup lang="ts">
2
+ import LockKey from '@iconify-icons/ph/lock-key-fill'
2
3
  import IconGoogle from '@iconify-icons/ri/google-fill'
3
- import { computed } from 'vue'
4
+ import { computed, ref } from 'vue'
5
+ import { usePower } from '../composables/Power'
4
6
  import SButton from './SButton.vue'
5
7
  import SLink from './SLink.vue'
8
+ import SLoginPagePasswordDialog from './SLoginPagePasswordDialog.vue'
9
+ import SModal from './SModal.vue'
6
10
  import SIconGbLogoWhite from './icon/SIconGbLogoWhite.vue'
7
11
 
8
12
  export interface CoverTitle {
@@ -15,11 +19,20 @@ export interface CoverPhotographer {
15
19
  link: string
16
20
  }
17
21
 
18
- export interface Action {
22
+ export type Action = ActionPassword | ActionSocial
23
+
24
+ export interface ActionPassword {
25
+ type: 'password'
26
+ onSubmit(email: string, password: string): Promise<boolean>
27
+ }
28
+
29
+ export interface ActionSocial {
19
30
  type: 'google'
20
- onClick: () => Promise<void>
31
+ onClick(): Promise<void>
21
32
  }
22
33
 
34
+ export type ActionType = 'password' | 'google'
35
+
23
36
  const props = defineProps<{
24
37
  cover: string
25
38
  coverTitle: CoverTitle
@@ -27,23 +40,51 @@ const props = defineProps<{
27
40
  actions: Action[]
28
41
  }>()
29
42
 
43
+ const passwordDialog = usePower()
44
+
45
+ const selectedPasswordAction = ref<ActionPassword | null>(null)
46
+ const actionInProgress = ref(false)
47
+ const actionError = ref(false)
48
+
30
49
  const coverBgImageStyle = computed(() => `url(${props.cover})`)
31
50
 
32
- function getActionLabel(type: Action['type']) {
51
+ function getActionLabel(type: ActionType) {
33
52
  switch (type) {
53
+ case 'password':
54
+ return 'Sign in via Password'
34
55
  case 'google':
35
56
  return 'Sign in via Google'
36
- default:
37
- throw new Error('[sefirot] Invalid action type')
38
57
  }
39
58
  }
40
59
 
41
- function getIconComponent(type: Action['type']) {
60
+ function getIconComponent(type: ActionType) {
42
61
  switch (type) {
62
+ case 'password':
63
+ return LockKey
43
64
  case 'google':
44
65
  return IconGoogle
45
- default:
46
- throw new Error('[sefirot] Invalid action type')
66
+ }
67
+ }
68
+
69
+ function onAction(action: Action) {
70
+ switch (action.type) {
71
+ case 'password':
72
+ selectedPasswordAction.value = action
73
+ return passwordDialog.on()
74
+ case 'google':
75
+ return action.onClick()
76
+ }
77
+ }
78
+
79
+ async function onSubmit(email: string, password: string) {
80
+ actionInProgress.value = true
81
+
82
+ actionError.value = !(await selectedPasswordAction.value!.onSubmit(email, password))
83
+
84
+ actionInProgress.value = false
85
+
86
+ if (!actionError.value) {
87
+ passwordDialog.off()
47
88
  }
48
89
  }
49
90
  </script>
@@ -74,20 +115,30 @@ function getIconComponent(type: Action['type']) {
74
115
  <p class="form-lead">This is a very closed login form meant for specific audiences only. If you can’t login, well, you know who to ask.</p>
75
116
  </div>
76
117
 
77
- <div class="form-actions">
78
- <SButton
79
- v-for="action in actions"
80
- :key="action.type"
81
- size="large"
82
- mode="white"
83
- rounded
84
- :label="getActionLabel(action.type)"
85
- :icon="getIconComponent(action.type)"
86
- @click="action.onClick"
87
- />
118
+ <div class="form-actions" :class="{ multi: actions.length > 1 }">
119
+ <div v-for="action in actions" :key="action.type" class="form-action">
120
+ <SButton
121
+ size="large"
122
+ mode="white"
123
+ block
124
+ rounded
125
+ :label="getActionLabel(action.type)"
126
+ :icon="getIconComponent(action.type)"
127
+ @click="onAction(action)"
128
+ />
129
+ </div>
88
130
  </div>
89
131
  </div>
90
132
  </div>
133
+
134
+ <SModal :open="passwordDialog.state.value" @close="passwordDialog.off">
135
+ <SLoginPagePasswordDialog
136
+ :loading="actionInProgress"
137
+ :error="actionError"
138
+ @cancel="passwordDialog.off"
139
+ @submit="onSubmit"
140
+ />
141
+ </SModal>
91
142
  </div>
92
143
  </template>
93
144
 
@@ -196,9 +247,15 @@ function getIconComponent(type: Action['type']) {
196
247
  display: flex;
197
248
  flex-direction: column;
198
249
  align-items: center;
199
- gap: 8px;
250
+ gap: 16px;
200
251
  padding-top: 24px;
201
252
  text-align: center;
202
253
  margin: 0 auto;
203
254
  }
255
+
256
+ .form-actions.multi .form-action {
257
+ display: block;
258
+ width: 100%;
259
+ max-width: 256px;
260
+ }
204
261
  </style>
@@ -0,0 +1,95 @@
1
+ <script setup lang="ts">
2
+ import { useD } from '../composables/D'
3
+ import { useV } from '../composables/V'
4
+ import { email, maxLength, required } from '../validation/rules'
5
+ import SAlert from './SAlert.vue'
6
+ import SCard from './SCard.vue'
7
+ import SCardBlock from './SCardBlock.vue'
8
+ import SContent from './SContent.vue'
9
+ import SControl from './SControl.vue'
10
+ import SControlButton from './SControlButton.vue'
11
+ import SControlRight from './SControlRight.vue'
12
+ import SDoc from './SDoc.vue'
13
+ import SInputText from './SInputText.vue'
14
+
15
+ defineProps<{
16
+ loading: boolean
17
+ error: boolean
18
+ }>()
19
+
20
+ const emit = defineEmits<{
21
+ cancel: []
22
+ submit: [email: string, password: string]
23
+ }>()
24
+
25
+ const { data } = useD({
26
+ email: null as string | null,
27
+ password: null as string | null
28
+ })
29
+
30
+ const { validation, validateAndNotify } = useV(data, {
31
+ email: {
32
+ required: required(),
33
+ maxLength: maxLength(255),
34
+ email: email()
35
+ },
36
+ password: {
37
+ required: required(),
38
+ maxLength: maxLength(255)
39
+ }
40
+ })
41
+
42
+ async function onSubmit() {
43
+ if (await validateAndNotify()) {
44
+ emit('submit', data.value.email!, data.value.password!)
45
+ }
46
+ }
47
+ </script>
48
+
49
+ <template>
50
+ <SCard size="small">
51
+ <SCardBlock class="s-p-24">
52
+ <SDoc>
53
+ <SContent>
54
+ <h2>Sign in to account</h2>
55
+ </SContent>
56
+ <SAlert v-if="error" mode="danger">
57
+ <p>Invalid email or password.</p>
58
+ </SAlert>
59
+ <SInputText
60
+ name="email"
61
+ type="email"
62
+ label="Email"
63
+ placeholder="john.doe@example.com"
64
+ v-model="data.email"
65
+ :validation="validation.email"
66
+ />
67
+ <SInputText
68
+ name="password"
69
+ type="password"
70
+ label="Password"
71
+ placeholder="Password"
72
+ v-model="data.password"
73
+ :validation="validation.password"
74
+ />
75
+ </SDoc>
76
+ </SCardBlock>
77
+ <SCardBlock size="xlarge" class="s-px-24">
78
+ <SControl>
79
+ <SControlRight>
80
+ <SControlButton
81
+ label="Cancel"
82
+ :disabled="loading"
83
+ @click="$emit('cancel')"
84
+ />
85
+ <SControlButton
86
+ mode="info"
87
+ label="Sign in"
88
+ :loading="loading"
89
+ @click="onSubmit"
90
+ />
91
+ </SControlRight>
92
+ </SControl>
93
+ </SCardBlock>
94
+ </SCard>
95
+ </template>
@@ -1,7 +1,7 @@
1
1
  import { isString } from '../../support/Utils'
2
2
 
3
3
  /* eslint-disable-next-line no-control-regex */
4
- const regExp = /^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|[\x01-\x09\x0B\x0C\x0E-\x7F])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21-\x5A\x53-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])+)\])$/i
4
+ const regExp = /^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|[\x01-\x09\x0B\x0C\x0E-\x7F])*")@(?:(?:[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21-\x5A\x53-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])+)\])$/i
5
5
 
6
6
  export function email(value: unknown): boolean {
7
7
  if (!isString(value)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "3.48.0",
3
+ "version": "3.50.0",
4
4
  "packageManager": "pnpm@9.1.1",
5
5
  "description": "Vue Components for Global Brain Design System.",
6
6
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",
@@ -52,6 +52,7 @@
52
52
  "@vuelidate/validators": "^2.0.4",
53
53
  "@vueuse/core": "^10.9.0",
54
54
  "body-scroll-lock": "4.0.0-beta.0",
55
+ "dayjs": "^1.11.11",
55
56
  "fuse.js": "^7.0.0",
56
57
  "lodash-es": "^4.17.21",
57
58
  "markdown-it": "^14.1.0",
@@ -70,7 +71,6 @@
70
71
  "@tinyhttp/cookie": "^2.1.0",
71
72
  "@types/file-saver": "^2.0.7",
72
73
  "@types/qs": "^6.9.15",
73
- "dayjs": "^1.11.11",
74
74
  "file-saver": "^2.0.5",
75
75
  "ofetch": "^1.3.4",
76
76
  "qs": "^6.12.1"
@@ -95,6 +95,7 @@
95
95
  "@vuelidate/validators": "^2.0.4",
96
96
  "@vueuse/core": "^10.9.0",
97
97
  "body-scroll-lock": "4.0.0-beta.0",
98
+ "dayjs": "^1.11.11",
98
99
  "eslint": "^8.57.0",
99
100
  "fuse.js": "^7.0.0",
100
101
  "happy-dom": "^14.10.2",