@mindful-web/marko-web-identity-x 1.0.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.
Files changed (138) hide show
  1. package/.eslintignore +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +200 -0
  4. package/api/fragment-types.json +1 -0
  5. package/api/fragments/active-user.js +108 -0
  6. package/api/mutations/add-external-user-id.js +24 -0
  7. package/api/mutations/create-app-user.js +11 -0
  8. package/api/mutations/delete-app-user-for-current-application.js +7 -0
  9. package/api/mutations/impersonate-app-user.js +12 -0
  10. package/api/mutations/logout-app-user.js +11 -0
  11. package/api/mutations/send-change-email-link.js +7 -0
  12. package/api/mutations/send-login-link.js +7 -0
  13. package/api/mutations/set-custom-attributes.js +15 -0
  14. package/api/queries/check-content-access.js +19 -0
  15. package/api/queries/get-active-context.js +45 -0
  16. package/api/queries/load-user-by-external-id.js +14 -0
  17. package/api/queries/load-user-by-id.js +11 -0
  18. package/api/queries/load-user.js +11 -0
  19. package/browser/.eslintrc.js +11 -0
  20. package/browser/access.vue +377 -0
  21. package/browser/authenticate.vue +278 -0
  22. package/browser/change-email-confirm.vue +120 -0
  23. package/browser/change-email-init.vue +179 -0
  24. package/browser/comments/create.vue +107 -0
  25. package/browser/comments/post.vue +151 -0
  26. package/browser/comments/stream.vue +319 -0
  27. package/browser/custom-column.vue +314 -0
  28. package/browser/dayjs.js +6 -0
  29. package/browser/download-related.vue +120 -0
  30. package/browser/download.vue +380 -0
  31. package/browser/errors/authentication.js +7 -0
  32. package/browser/errors/feature.js +7 -0
  33. package/browser/errors/form.js +7 -0
  34. package/browser/errors/logout.js +7 -0
  35. package/browser/form/address-block.vue +131 -0
  36. package/browser/form/common/checkbox-group.vue +140 -0
  37. package/browser/form/common/form-group.vue +24 -0
  38. package/browser/form/common/form-label.vue +17 -0
  39. package/browser/form/consent.vue +121 -0
  40. package/browser/form/fields/address-extra.vue +68 -0
  41. package/browser/form/fields/city.vue +68 -0
  42. package/browser/form/fields/comment-body.vue +63 -0
  43. package/browser/form/fields/country.vue +94 -0
  44. package/browser/form/fields/custom-boolean.vue +58 -0
  45. package/browser/form/fields/custom-select-group.vue +25 -0
  46. package/browser/form/fields/custom-select-multiple.vue +97 -0
  47. package/browser/form/fields/custom-select-write-in-inline.vue +39 -0
  48. package/browser/form/fields/custom-select-write-in.vue +66 -0
  49. package/browser/form/fields/custom-select.vue +191 -0
  50. package/browser/form/fields/custom-text.vue +62 -0
  51. package/browser/form/fields/display-name.vue +64 -0
  52. package/browser/form/fields/email.vue +62 -0
  53. package/browser/form/fields/family-name.vue +68 -0
  54. package/browser/form/fields/given-name.vue +68 -0
  55. package/browser/form/fields/organization-title.vue +68 -0
  56. package/browser/form/fields/organization.vue +68 -0
  57. package/browser/form/fields/phone-number.vue +68 -0
  58. package/browser/form/fields/postal-code.vue +68 -0
  59. package/browser/form/fields/receive-email.vue +58 -0
  60. package/browser/form/fields/region.vue +106 -0
  61. package/browser/form/fields/regional-policy.vue +59 -0
  62. package/browser/form/fields/street.vue +68 -0
  63. package/browser/graphql/create-client.js +65 -0
  64. package/browser/graphql/parse.js +1 -0
  65. package/browser/graphql/queries/download-related-content.js +38 -0
  66. package/browser/index.js +110 -0
  67. package/browser/login-fields.vue +214 -0
  68. package/browser/login.vue +331 -0
  69. package/browser/logout.vue +68 -0
  70. package/browser/mixins/global-auto-signup-event-emitter.js +18 -0
  71. package/browser/mixins/global-event-emitter.js +28 -0
  72. package/browser/profile.vue +688 -0
  73. package/browser/service.js +32 -0
  74. package/browser/utils/clean-path.js +1 -0
  75. package/browser/utils/cookies-enabled.js +10 -0
  76. package/browser/utils/get-as-array.js +6 -0
  77. package/browser/utils/get-referring-page.js +7 -0
  78. package/browser/utils/get.js +8 -0
  79. package/browser/utils/post.js +10 -0
  80. package/browser/utils/redirect.js +7 -0
  81. package/browser/utils/region-country-codes.js +1 -0
  82. package/components/access.marko +24 -0
  83. package/components/access.marko.js +61 -0
  84. package/components/comment-stream.marko +31 -0
  85. package/components/comment-stream.marko.js +71 -0
  86. package/components/context.marko +15 -0
  87. package/components/context.marko.js +52 -0
  88. package/components/form-access.marko +60 -0
  89. package/components/form-access.marko.js +111 -0
  90. package/components/form-authenticate.marko +32 -0
  91. package/components/form-authenticate.marko.js +73 -0
  92. package/components/form-change-email.marko +27 -0
  93. package/components/form-change-email.marko.js +68 -0
  94. package/components/form-login.marko +32 -0
  95. package/components/form-login.marko.js +72 -0
  96. package/components/form-logout.marko +7 -0
  97. package/components/form-logout.marko.js +36 -0
  98. package/components/form-profile.marko +38 -0
  99. package/components/form-profile.marko.js +78 -0
  100. package/components/form-register.marko +2 -0
  101. package/components/form-register.marko.js +32 -0
  102. package/components/identify.marko +17 -0
  103. package/components/identify.marko.js +59 -0
  104. package/components/marko.json +90 -0
  105. package/components/non-auth-identify.marko +15 -0
  106. package/components/non-auth-identify.marko.js +53 -0
  107. package/config.js +161 -0
  108. package/hooks.js +20 -0
  109. package/index.js +30 -0
  110. package/marko.json +6 -0
  111. package/middleware/content-access-state.js +31 -0
  112. package/middleware/content-download-state.js +27 -0
  113. package/middleware.js +32 -0
  114. package/package.json +40 -0
  115. package/routes/access.js +29 -0
  116. package/routes/authenticate.js +58 -0
  117. package/routes/change-email-confirm.js +60 -0
  118. package/routes/change-email-init.js +16 -0
  119. package/routes/comment-count.js +19 -0
  120. package/routes/comments.js +43 -0
  121. package/routes/countries.js +19 -0
  122. package/routes/create-comment.js +25 -0
  123. package/routes/download.js +29 -0
  124. package/routes/flag-comment.js +21 -0
  125. package/routes/index.js +40 -0
  126. package/routes/login-fields.js +35 -0
  127. package/routes/login.js +95 -0
  128. package/routes/logout.js +19 -0
  129. package/routes/profile.js +183 -0
  130. package/routes/regions.js +22 -0
  131. package/service.js +483 -0
  132. package/utils/call-hooks-for.js +28 -0
  133. package/utils/context-cookie.js +30 -0
  134. package/utils/create-client.js +48 -0
  135. package/utils/create-identity-builder.js +33 -0
  136. package/utils/get-as-array.js +6 -0
  137. package/utils/json-error-handler.js +41 -0
  138. package/utils/token-cookie.js +30 -0
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <div class="download-related__wrapper">
3
+ <p v-if="items.length">
4
+ Based on your interest in this {{ content.type }}, we'd recommend these items for you:
5
+ </p>
6
+ <div v-for="item in items" :key="item.id" class="download-related__item">
7
+ <div class="download-related__image">
8
+ <img
9
+ v-if="item.primaryImage"
10
+ :src="imgixUrl(item.primaryImage.src)"
11
+ :alt="item.primaryImage.alt"
12
+ >
13
+ </div>
14
+ <div class="download-related__title">
15
+ <h3 v-html="item.name" />
16
+ <small v-if="item.company">
17
+ from <strong>{{ item.company.name }}</strong>
18
+ </small>
19
+ </div>
20
+ <div class="download-related__button">
21
+ <button type="button" class="btn btn-primary" @click="$emit('submit', item)">
22
+ Download
23
+ </button>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ import getAsArray from './utils/get-as-array';
31
+ import allPublishedContent from './graphql/queries/download-related-content';
32
+
33
+ export default {
34
+
35
+ inject: ['$graphql'],
36
+
37
+ props: {
38
+ content: {
39
+ type: Object,
40
+ default: () => ({}),
41
+ },
42
+ limit: {
43
+ type: Number,
44
+ default: 4,
45
+ },
46
+ types: {
47
+ type: Array,
48
+ default: () => (['Document', 'Whitepaper']),
49
+ },
50
+ },
51
+
52
+ data: () => ({
53
+ loading: false,
54
+ items: [],
55
+ }),
56
+
57
+ mounted() {
58
+ this.load();
59
+ },
60
+
61
+ methods: {
62
+ imgixUrl(url) {
63
+ const Url = new URL(url);
64
+ const usp = Url.searchParams;
65
+ usp.set('h', 107);
66
+ usp.set('w', 160);
67
+ usp.set('fit', 'crop');
68
+ return Url;
69
+ },
70
+ async load() {
71
+ this.loading = true;
72
+ try {
73
+ // Start with directly related items
74
+ const items = [
75
+ ...getAsArray(this.content, 'relatedTo.edges')
76
+ .map(({ node }) => node)
77
+ .slice(0, this.limit),
78
+ ];
79
+ // Get from primary section
80
+ if (items.length < this.limit) {
81
+ const limit = this.limit - items.length;
82
+ const input = {
83
+ sectionId: this.content.primarySection.id,
84
+ includeContentTypes: this.types,
85
+ excludeContentIds: items.map((doc) => doc.id),
86
+ pagination: { limit },
87
+ };
88
+ const data = await this.$graphql.query({
89
+ query: allPublishedContent,
90
+ variables: { input },
91
+ // headers: {},
92
+ });
93
+ getAsArray(data, 'data.allPublishedContent.edges').forEach((doc) => items.push(doc.node));
94
+ }
95
+ // Get latest published items
96
+ if (items.length < this.limit) {
97
+ const limit = this.limit - items.length;
98
+ const input = {
99
+ sectionId: this.content.primarySection.id,
100
+ includeContentTypes: this.types,
101
+ excludeContentIds: items.map((doc) => doc.id),
102
+ pagination: { limit },
103
+ };
104
+ const data = await this.$graphql.query({
105
+ query: allPublishedContent,
106
+ variables: { input },
107
+ // headers: {},
108
+ });
109
+ getAsArray(data, 'data.allPublishedContent.edges').forEach((doc) => items.push(doc.node));
110
+ }
111
+ this.items = items;
112
+ } catch (e) {
113
+ this.error = e.message;
114
+ } finally {
115
+ this.loading = false;
116
+ }
117
+ },
118
+ },
119
+ };
120
+ </script>
@@ -0,0 +1,380 @@
1
+ <template>
2
+ <div id="content-download-idx-form" class="content-page-gate p-block">
3
+ <template v-if="hasActiveUser">
4
+ <h5 class="content-page-gate__title">
5
+ {{ title }}
6
+ </h5>
7
+ <p v-if="!didSubmit && displayForm" v-html="callToAction" />
8
+ <form v-if="!didSubmit && displayForm" @submit.prevent="handleSubmit">
9
+ <fieldset :disabled="isLoading">
10
+ <div v-for="(row, ridx) in fieldRows" :key="ridx" class="row">
11
+ <custom-column
12
+ v-for="(col, cidx) in row"
13
+ :key="`${ridx}_${cidx}`"
14
+ :label="col.label || defaultFieldLabels[col.key]"
15
+ :field-key="col.key"
16
+ :field-id="col.id"
17
+ :type="col.type"
18
+ :required="col.required"
19
+ :width="col.width || 1"
20
+ :user="user"
21
+ :endpoints="endpoints"
22
+ :enable-change-email="enableChangeEmail"
23
+ />
24
+ </div>
25
+
26
+ <form-consent
27
+ :user="user"
28
+ :consent-policy="consentPolicy"
29
+ :consent-policy-enabled="consentPolicyEnabled"
30
+ :email-consent-request="emailConsentRequest"
31
+ :email-consent-request-enabled="emailConsentRequestEnabled"
32
+ :regional-consent-policies="regionalConsentPolicies"
33
+ :country-code="countryCode"
34
+ />
35
+
36
+ <div class="d-flex align-items-center">
37
+ <button type="submit" class="btn btn-primary">
38
+ {{ buttonLabel }}
39
+ </button>
40
+ </div>
41
+ </fieldset>
42
+ <p v-if="error" class="mt-3 text-danger">
43
+ An error occurred: {{ error }}
44
+ </p>
45
+ </form>
46
+
47
+ <template v-else>
48
+ <div class="success-message">
49
+ <p class="success-message__title">
50
+ Your responses have been saved, and your download should begin automatically.
51
+ </p>
52
+ <p class="success-message__title d-flex justify-content-between">
53
+ If not, click this button to start:
54
+ <a
55
+ class="btn btn-primary mr-3"
56
+ :href="content.fileSrc"
57
+ target="_blank"
58
+ >Download</a>
59
+ </p>
60
+ <download-related v-if="showRelated" :content="content" @submit="download" />
61
+ </div>
62
+ </template>
63
+ </template>
64
+
65
+ <div v-else>
66
+ <p v-html="callToActionLoggedOut" />
67
+ <login
68
+ :additional-event-data="additionalEventData"
69
+ :source="loginSource"
70
+ :endpoints="endpoints"
71
+ :redirect="redirect"
72
+ :app-context-id="appContextId"
73
+ :consent-policy="consentPolicy"
74
+ :consent-policy-enabled="consentPolicyEnabled"
75
+ :email-consent-request="emailConsentRequest"
76
+ :email-consent-request-enabled="emailConsentRequestEnabled"
77
+ :regional-consent-policies="regionalConsentPolicies"
78
+ :required-create-fields="requiredCreateFields"
79
+ :default-field-labels="defaultFieldLabels"
80
+ />
81
+ </div>
82
+ </div>
83
+ </template>
84
+
85
+ <script>
86
+ import post from './utils/post';
87
+ import cookiesEnabled from './utils/cookies-enabled';
88
+
89
+ import FormConsent from './form/consent.vue';
90
+ import CustomColumn from './custom-column.vue';
91
+ import Login from './login.vue';
92
+
93
+ import FeatureError from './errors/feature';
94
+ import FormError from './errors/form';
95
+ import EventEmitter from './mixins/global-event-emitter';
96
+ import DownloadRelated from './download-related.vue';
97
+
98
+ export default {
99
+ components: {
100
+ CustomColumn,
101
+ DownloadRelated,
102
+ FormConsent,
103
+ Login,
104
+ },
105
+
106
+ /**
107
+ *
108
+ */
109
+ mixins: [EventEmitter],
110
+
111
+ /**
112
+ *
113
+ */
114
+ props: {
115
+ /**
116
+ * custom form props
117
+ */
118
+ content: {
119
+ type: Object,
120
+ required: true,
121
+ },
122
+ fieldRows: {
123
+ type: Array,
124
+ required: true,
125
+ },
126
+ title: {
127
+ type: String,
128
+ default: 'Complete the form to download this content',
129
+ },
130
+ callToAction: {
131
+ type: String,
132
+ default: 'To download this content, please fill out the form below.',
133
+ },
134
+ callToActionLoggedOut: {
135
+ type: String,
136
+ default: 'To download this content, please enter your email address below.',
137
+ },
138
+ cookie: {
139
+ type: Object,
140
+ required: true,
141
+ },
142
+ displayForm: {
143
+ type: Boolean,
144
+ default: true,
145
+ },
146
+
147
+ /**
148
+ * profile/login props
149
+ */
150
+ loginSource: {
151
+ type: String,
152
+ default: 'contentDownload',
153
+ },
154
+ endpoints: {
155
+ type: Object,
156
+ required: true,
157
+ },
158
+ activeUser: {
159
+ type: Object,
160
+ default: () => ({}),
161
+ },
162
+ requiredCreateFields: {
163
+ type: Array,
164
+ default: () => [],
165
+ },
166
+ defaultFieldLabels: {
167
+ type: Object,
168
+ default: () => {},
169
+ },
170
+ consentPolicy: {
171
+ type: String,
172
+ default: null,
173
+ },
174
+ consentPolicyEnabled: {
175
+ type: Boolean,
176
+ default: false,
177
+ },
178
+ emailConsentRequest: {
179
+ type: String,
180
+ default: null,
181
+ },
182
+ emailConsentRequestEnabled: {
183
+ type: Boolean,
184
+ default: false,
185
+ },
186
+ appContextId: {
187
+ type: String,
188
+ default: null,
189
+ },
190
+ buttonLabel: {
191
+ type: String,
192
+ default: 'Submit & Download',
193
+ },
194
+ regionalConsentPolicies: {
195
+ type: Array,
196
+ default: () => [],
197
+ },
198
+ defaultCountryCode: {
199
+ type: String,
200
+ default: null,
201
+ },
202
+ enableChangeEmail: {
203
+ type: Boolean,
204
+ default: false,
205
+ },
206
+ showRelated: {
207
+ type: Boolean,
208
+ default: true,
209
+ },
210
+ updateProfileOnSubmit: {
211
+ type: Boolean,
212
+ default: true,
213
+ },
214
+ },
215
+
216
+ /**
217
+ *
218
+ */
219
+ data() {
220
+ return {
221
+ error: null,
222
+ isLoading: false,
223
+ isReloadingPage: false,
224
+ isRedirectingPage: false,
225
+ didSubmit: false,
226
+ user: {
227
+ ...this.activeUser,
228
+ ...(this.defaultCountryCode
229
+ && !this.activeUser.countryCode
230
+ && { countryCode: this.defaultCountryCode }
231
+ ),
232
+ },
233
+ downloaded: [],
234
+ };
235
+ },
236
+
237
+ /**
238
+ *
239
+ */
240
+ computed: {
241
+ /**
242
+ *
243
+ */
244
+ hasActiveUser() {
245
+ return this.user && this.user.email;
246
+ },
247
+
248
+ /**
249
+ *
250
+ */
251
+ redirect() {
252
+ if (this.content.siteContext && this.content.siteContext.url) {
253
+ return this.content.siteContext.url;
254
+ }
255
+ if (this.content) {
256
+ return `/${this.content.id}`;
257
+ }
258
+ return '/';
259
+ },
260
+
261
+ /**
262
+ *
263
+ */
264
+ canUpdateProfile() {
265
+ return this.hasActiveUser && this.updateProfileOnSubmit;
266
+ },
267
+
268
+ /**
269
+ *
270
+ */
271
+ countryCode() {
272
+ if (this.user && this.user.countryCode) return this.user.countryCode;
273
+ return this.defaultCountryCode;
274
+ },
275
+ },
276
+
277
+ /**
278
+ *
279
+ */
280
+ mounted() {
281
+ if (cookiesEnabled()) {
282
+ this.emit('download-mounted');
283
+ } else {
284
+ const error = new FeatureError('Your browser does not support cookies. Please enable cookies to use this feature.');
285
+ this.error = error.message;
286
+ this.emit('download-errored', { message: error.message });
287
+ }
288
+ },
289
+
290
+ /**
291
+ *
292
+ */
293
+ methods: {
294
+ /**
295
+ *
296
+ */
297
+ async handleSubmit() {
298
+ this.error = null;
299
+ this.isLoading = true;
300
+ this.didSubmit = false;
301
+ try {
302
+ const additionalEventData = { ...this.additionalEventData, actionSource: this.loginSource };
303
+ let data = {};
304
+ if (this.canUpdateProfile) {
305
+ const res = await post('/profile', { ...this.user, additionalEventData });
306
+ data = await res.json();
307
+ if (!res.ok) throw new FormError(data.message, res.status);
308
+ this.user = data.user;
309
+ }
310
+
311
+ // Re-focus on the form
312
+ document.getElementById('content-download-idx-form').scrollIntoView({ behavior: 'smooth' });
313
+
314
+ // Perform and notify about the download
315
+ const eventData = { ...additionalEventData, ...(data.additionalEventData || {}) };
316
+ await this.download(this.content, eventData);
317
+ this.didSubmit = true;
318
+ } catch (e) {
319
+ this.error = e;
320
+ this.emit('download-errored', { message: e.message });
321
+ } finally {
322
+ this.isLoading = false;
323
+ }
324
+ },
325
+ /**
326
+ *
327
+ */
328
+ async download(content, additionalEventData) {
329
+ // Only emit event once when downloading
330
+ if (!this.downloaded.includes(content.id)) {
331
+ const company = content.company || {};
332
+ const { entity } = await post('/download', {
333
+ contentId: content.id,
334
+ contentType: content.type,
335
+ cookie: this.cookie,
336
+ companyId: company.id,
337
+ userId: this.user.id,
338
+ additionalEventData,
339
+ // Flatten the payload for storage
340
+ payload: this.fieldRows.reduce((objs, row) => {
341
+ const { fields, selects, booleans } = objs;
342
+ row.forEach((col) => {
343
+ const { id, type, label } = col;
344
+ if (type === 'custom-select') {
345
+ const value = this.user.customSelectFieldAnswers.find((ans) => ans.id === id);
346
+ const values = (value.answers || []).map((answer) => {
347
+ const opts = value.field.options.reduce((arr, opt) => ([
348
+ ...arr, opt, ...(opt.options ? opt.options : []),
349
+ ]), []);
350
+ const ans = opts.find((opt) => opt.id === answer.id);
351
+ return { id: answer.id, label: ans.label, writeIn: answer.writeInValue };
352
+ });
353
+ selects[id] = { label, values };
354
+ } else if (type === 'custom-boolean') {
355
+ const value = this.user.customBooleanFieldAnswers.find((ans) => ans.id === id);
356
+ booleans[id] = { label, value };
357
+ } else {
358
+ const value = this.user[col.key];
359
+ fields[col.key] = { label, value };
360
+ }
361
+ });
362
+ return { fields, selects, booleans };
363
+ }, { fields: {}, selects: {}, booleans: {} }),
364
+ });
365
+ this.emit('download-submitted', {
366
+ contentId: content.id,
367
+ contentType: content.type,
368
+ companyId: company.id,
369
+ userId: this.user.id,
370
+ additionalEventData,
371
+ }, entity);
372
+ this.downloaded.push(content.id);
373
+ }
374
+
375
+ // Attempt to open download
376
+ window.open(content.fileSrc, '_blank');
377
+ },
378
+ },
379
+ };
380
+ </script>
@@ -0,0 +1,7 @@
1
+ export default class AuthenticationError extends Error {
2
+ constructor(message, code = 500) {
3
+ super(message);
4
+ this.name = 'AuthenticationError';
5
+ this.code = code;
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ export default class FeatureError extends Error {
2
+ constructor(message, code = 400) {
3
+ super(message);
4
+ this.name = 'FeatureError';
5
+ this.code = code;
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ export default class FormError extends Error {
2
+ constructor(message, code = 500) {
3
+ super(message);
4
+ this.name = 'FormError';
5
+ this.code = code;
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ export default class LogoutError extends Error {
2
+ constructor(message, code = 500) {
3
+ super(message);
4
+ this.name = 'LogoutError';
5
+ this.code = code;
6
+ }
7
+ }
@@ -0,0 +1,131 @@
1
+ <template>
2
+ <fieldset v-if="showBlock" class="px-3 border mb-3 address-block">
3
+ <legend class="h6 w-auto">
4
+ {{ blockLabel }}
5
+ </legend>
6
+
7
+ <div v-if="street.visible" class="row">
8
+ <street
9
+ v-model="user.street"
10
+ :required="street.required"
11
+ :class-name="addressExtra.visible ? 'col-md-8' : 'col-md-12'"
12
+ :label="defaultFieldLabels.street"
13
+ />
14
+ <address-extra
15
+ v-if="addressExtra.visible"
16
+ v-model="user.addressExtra"
17
+ :required="addressExtra.required"
18
+ :label="defaultFieldLabels.addressExtra"
19
+ />
20
+ </div>
21
+
22
+ <div v-if="city.visible || regionCode.visible || postalCode.visible" class="row">
23
+ <city
24
+ v-if="city.visible"
25
+ v-model="user.city"
26
+ :required="city.required"
27
+ :class-name="addressLineTwoFieldClass"
28
+ :label="defaultFieldLabels.city"
29
+ />
30
+ <region
31
+ v-if="regionCode.visible"
32
+ v-model="user.regionCode"
33
+ :country-code="user.countryCode"
34
+ :required="regionCode.required"
35
+ :class-name="addressLineTwoFieldClass"
36
+ :label="defaultFieldLabels.region"
37
+ />
38
+ <postal-code
39
+ v-if="postalCode.visible"
40
+ v-model="user.postalCode"
41
+ :required="postalCode.required"
42
+ :class-name="addressLineTwoFieldClass"
43
+ :label="defaultFieldLabels.postalCode"
44
+ />
45
+ </div>
46
+ </fieldset>
47
+ </template>
48
+
49
+ <script>
50
+ import City from './fields/city.vue';
51
+ import Region from './fields/region.vue';
52
+ import PostalCode from './fields/postal-code.vue';
53
+ import Street from './fields/street.vue';
54
+ import AddressExtra from './fields/address-extra.vue';
55
+
56
+ export default {
57
+ components: {
58
+ City,
59
+ Region,
60
+ PostalCode,
61
+ Street,
62
+ AddressExtra,
63
+ },
64
+ props: {
65
+ user: {
66
+ type: Object,
67
+ required: true,
68
+ },
69
+ defaultFieldLabels: {
70
+ type: Object,
71
+ default: () => {},
72
+ },
73
+ street: {
74
+ type: Object,
75
+ required: true,
76
+ },
77
+ addressExtra: {
78
+ type: Object,
79
+ required: true,
80
+ },
81
+ city: {
82
+ type: Object,
83
+ required: true,
84
+ },
85
+ regionCode: {
86
+ type: Object,
87
+ required: true,
88
+ },
89
+ postalCode: {
90
+ type: Object,
91
+ required: true,
92
+ },
93
+ },
94
+ /**
95
+ *
96
+ */
97
+ computed: {
98
+ addressLineTwoFieldClass() {
99
+ let fields = 0;
100
+ if (this.city.visible) fields += 1;
101
+ if (this.regionCode.visible) fields += 1;
102
+ if (this.postalCode.visible) fields += 1;
103
+ if (fields === 0 || fields === 1) return 'col-md-12';
104
+ return `col-md-${12 / fields}`;
105
+ },
106
+ showBlock() {
107
+ return [
108
+ this.street,
109
+ this.addressExtra,
110
+ this.city,
111
+ this.regionCode,
112
+ this.postalCode,
113
+ ].some((v) => v.visible);
114
+ },
115
+ classNames() {
116
+ const classNames = ['form-group'];
117
+ const { className } = this;
118
+ if (className) classNames.push(className);
119
+ return classNames;
120
+ },
121
+ countryCode() {
122
+ const { user } = this;
123
+ if (!user) return null;
124
+ return user.countryCode;
125
+ },
126
+ blockLabel() {
127
+ return this.defaultFieldLabels.addressBlockLabel || 'Address';
128
+ },
129
+ },
130
+ };
131
+ </script>