@kiva/kv-components 10.7.0 → 10.9.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.
@@ -200,6 +200,18 @@ const d = {
200
200
  type: Number,
201
201
  default: 0
202
202
  },
203
+ /**
204
+ * Opt-in line cap for the loan use statement. Unset, the light view behaves as before: a 4-line
205
+ * CSS clamp plus the `truncateWordsNumber` word cut. Set, a statement that runs past the cap is
206
+ * trimmed so "… read more" stays visible; one that fills the cap on its own is shown whole
207
+ * without the link. The `light-detailed` variant always clamps to 3 lines and ignores this prop.
208
+ */
209
+ loanUseMaxLines: {
210
+ type: Number,
211
+ default: null,
212
+ validator: (e) => e === null || e > 0
213
+ // 0 would hide the whole statement
214
+ },
203
215
  showRefreshButton: {
204
216
  type: Boolean,
205
217
  default: !1
@@ -236,8 +248,8 @@ const d = {
236
248
  distributionModel: v,
237
249
  formattedLocation: g,
238
250
  fundraisingPercent: B,
239
- hasProgressData: _,
240
- imageHash: N,
251
+ hasProgressData: N,
252
+ imageHash: _,
241
253
  isLoading: T,
242
254
  loanAmount: f,
243
255
  loanBorrowerCount: L,
@@ -245,9 +257,9 @@ const d = {
245
257
  loanStatus: y,
246
258
  loanUse: A,
247
259
  readMorePath: R,
248
- tag: F,
260
+ tag: M,
249
261
  unreservedAmount: u,
250
- sharesAvailable: M
262
+ sharesAvailable: F
251
263
  } = Z(e), { clickReadMore: K } = ee(e, o), P = a(() => c.value ? void 0 : { width: "100px", height: "100px" }), D = a(() => {
252
264
  if (!e.website)
253
265
  return "";
@@ -269,7 +281,7 @@ const d = {
269
281
  loanStatus: y.value,
270
282
  loanUse: A.value
271
283
  });
272
- }), O = a(() => i.value.borrowerNameWithCountry), U = a(() => i.value.loanUsePrefixStart), V = a(() => i.value.loanUsePrefixEnd), x = a(() => i.value.statementTitle), I = a(() => i.value.visibleUseStatement), C = a(() => {
284
+ }), U = a(() => i.value.borrowerNameWithCountry), O = a(() => i.value.loanUsePrefixStart), V = a(() => i.value.loanUsePrefixEnd), x = a(() => i.value.statementTitle), I = a(() => i.value.visibleUseStatement), C = a(() => {
273
285
  var r;
274
286
  const t = parseFloat(u.value);
275
287
  if (!t) return [];
@@ -310,14 +322,14 @@ const d = {
310
322
  amountOptions: C,
311
323
  borrowerImageStyle: P,
312
324
  borrowerName: h,
313
- borrowerNameWithCountry: O,
325
+ borrowerNameWithCountry: U,
314
326
  lightDetailedAmount: G,
315
327
  distributionModel: v,
316
328
  formattedLocation: g,
317
329
  formattedWebsite: D,
318
330
  fundraisingPercent: B,
319
- hasProgressData: _,
320
- imageHash: N,
331
+ hasProgressData: N,
332
+ imageHash: _,
321
333
  isLightDetailedVariant: s,
322
334
  isLoading: T,
323
335
  isPostGoalVariant: m,
@@ -327,15 +339,15 @@ const d = {
327
339
  loanStatus: y,
328
340
  loanUse: A,
329
341
  loanUsePrefixEnd: V,
330
- loanUsePrefixStart: U,
342
+ loanUsePrefixStart: O,
331
343
  mdiLink: J,
332
344
  mdiCached: z,
333
345
  mdiArrowRight: H,
334
346
  readMorePath: R,
335
347
  shouldShowRefreshButton: w,
336
- tag: F,
348
+ tag: M,
337
349
  unreservedAmount: u,
338
- sharesAvailable: M,
350
+ sharesAvailable: F,
339
351
  selectedAmount: l,
340
352
  statementTitle: x,
341
353
  customStyle: E,
@@ -132,6 +132,11 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
132
132
  type: NumberConstructor;
133
133
  default: number;
134
134
  };
135
+ loanUseMaxLines: {
136
+ type: NumberConstructor;
137
+ default: any;
138
+ validator: (value: number | null) => boolean;
139
+ };
135
140
  showRefreshButton: {
136
141
  type: BooleanConstructor;
137
142
  default: boolean;
@@ -326,6 +331,11 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
326
331
  type: NumberConstructor;
327
332
  default: number;
328
333
  };
334
+ loanUseMaxLines: {
335
+ type: NumberConstructor;
336
+ default: any;
337
+ validator: (value: number | null) => boolean;
338
+ };
329
339
  showRefreshButton: {
330
340
  type: BooleanConstructor;
331
341
  default: boolean;
@@ -377,6 +387,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
377
387
  website: string;
378
388
  showLoanUse: boolean;
379
389
  showLightView: boolean;
390
+ loanUseMaxLines: number;
380
391
  showRefreshButton: boolean;
381
392
  customAmountLent: string;
382
393
  }, {}, {
@@ -503,13 +514,22 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
503
514
  type: NumberConstructor;
504
515
  default: number;
505
516
  };
517
+ maxLines: {
518
+ type: NumberConstructor;
519
+ default: any;
520
+ validator: (value: number | null) => boolean;
521
+ };
506
522
  }>, {}, {}, {
523
+ hasMaxLines(): boolean;
524
+ fitsReadMore(): boolean;
507
525
  helpLanguage(): "helps" | "helped";
508
526
  isDirect(): boolean;
509
527
  whySpecialSentence(): string;
510
528
  nameSpan(): string;
511
529
  loanUse(): string;
512
- }, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
530
+ }, {
531
+ fitReadMore(): void;
532
+ }, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
513
533
  anonymizationLevel: {
514
534
  type: StringConstructor;
515
535
  default: string;
@@ -570,12 +590,17 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
570
590
  type: NumberConstructor;
571
591
  default: number;
572
592
  };
593
+ maxLines: {
594
+ type: NumberConstructor;
595
+ default: any;
596
+ validator: (value: number | null) => boolean;
597
+ };
573
598
  }>> & Readonly<{}>, {
574
599
  name: string;
575
600
  use: string;
601
+ status: string;
576
602
  anonymizationLevel: string;
577
603
  loanAmount: string;
578
- status: string;
579
604
  borrowerCount: number;
580
605
  distributionModel: string;
581
606
  whySpecial: string;
@@ -586,6 +611,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
586
611
  useIndicativeHelpText: boolean;
587
612
  showReadMore: boolean;
588
613
  truncateWordsNumber: number;
614
+ maxLines: number;
589
615
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
590
616
  KvMaterialIcon: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
591
617
  icon: {
@@ -58,8 +58,8 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
58
58
  onFocus?: (...args: any[]) => any;
59
59
  onBlur?: (...args: any[]) => any;
60
60
  onOpen?: (...args: any[]) => any;
61
- onClosed?: (...args: any[]) => any;
62
61
  "onUpdate:model-value"?: (...args: any[]) => any;
62
+ onClosed?: (...args: any[]) => any;
63
63
  "onUpdate:model-timezone-value"?: (...args: any[]) => any;
64
64
  "onText-submit"?: (...args: any[]) => any;
65
65
  onCleared?: (...args: any[]) => any;
@@ -93,8 +93,8 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
93
93
  onFocus?: (...args: any[]) => any;
94
94
  onBlur?: (...args: any[]) => any;
95
95
  onOpen?: (...args: any[]) => any;
96
- onClosed?: (...args: any[]) => any;
97
96
  "onUpdate:model-value"?: (...args: any[]) => any;
97
+ onClosed?: (...args: any[]) => any;
98
98
  "onUpdate:model-timezone-value"?: (...args: any[]) => any;
99
99
  "onText-submit"?: (...args: any[]) => any;
100
100
  onCleared?: (...args: any[]) => any;
@@ -125,8 +125,8 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
125
125
  onFocus?: (...args: any[]) => any;
126
126
  onBlur?: (...args: any[]) => any;
127
127
  onOpen?: (...args: any[]) => any;
128
- onClosed?: (...args: any[]) => any;
129
128
  "onUpdate:model-value"?: (...args: any[]) => any;
129
+ onClosed?: (...args: any[]) => any;
130
130
  "onUpdate:model-timezone-value"?: (...args: any[]) => any;
131
131
  "onText-submit"?: (...args: any[]) => any;
132
132
  onCleared?: (...args: any[]) => any;
@@ -0,0 +1 @@
1
+ .kv-loan-use-lines[data-v-534d5054]{-webkit-line-clamp:var(--kv-loan-use-lines);line-clamp:var(--kv-loan-use-lines)}
@@ -1,16 +1,18 @@
1
- import o from "./KvLoanUse.script.js";
2
- import { KV_LOAN_USE_FRAGMENT as E } from "./KvLoanUse.script.js";
3
- import { openBlock as r, createElementBlock as n } from "vue";
4
- import t from "../_virtual/_plugin-vue_export-helper.js";
5
- const c = ["innerHTML"];
6
- function s(_, p, a, l, m, e) {
7
- return r(), n("p", {
8
- class: "tw-line-clamp-4",
1
+ import a from "./KvLoanUse.script.js";
2
+ import { KV_LOAN_USE_FRAGMENT as k } from "./KvLoanUse.script.js";
3
+ import { openBlock as s, createElementBlock as o, normalizeStyle as r, normalizeClass as t } from "vue";
4
+ import "./KvLoanUse.css";
5
+ import l from "../_virtual/_plugin-vue_export-helper.js";
6
+ const i = ["innerHTML"];
7
+ function c(_, m, n, p, d, e) {
8
+ return s(), o("p", {
9
+ class: t(["tw-line-clamp-4", { "kv-loan-use-lines": e.hasMaxLines }]),
10
+ style: r(e.hasMaxLines ? { "--kv-loan-use-lines": n.maxLines } : void 0),
9
11
  innerHTML: e.loanUse
10
- }, null, 8, c);
12
+ }, null, 14, i);
11
13
  }
12
- const u = /* @__PURE__ */ t(o, [["render", s]]);
14
+ const v = /* @__PURE__ */ l(a, [["render", c], ["__scopeId", "data-v-534d5054"]]);
13
15
  export {
14
- E as KV_LOAN_USE_FRAGMENT,
15
- u as default
16
+ k as KV_LOAN_USE_FRAGMENT,
17
+ v as default
16
18
  };
@@ -1,7 +1,8 @@
1
- import u from "graphql-tag";
1
+ import h from "graphql-tag";
2
2
  import l from "numeral";
3
- import { truncateStringByWords as i } from "../utils/loanUtils.js";
4
- const r = "direct", o = (e) => e.charAt(0).toUpperCase() + e.slice(1), t = (e) => e.charAt(0).toLowerCase() + e.slice(1), m = u`
3
+ import { truncateStringByWords as a } from "../utils/loanUtils.js";
4
+ import { fitStatement as d, READ_MORE_CLASS as r } from "../utils/loanUseFit.js";
5
+ const o = "direct", u = (e) => e.charAt(0).toUpperCase() + e.slice(1), t = (e) => e.charAt(0).toLowerCase() + e.slice(1), w = h`
5
6
  fragment KvLoanUse on LoanBasic {
6
7
  id
7
8
  anonymizationLevel
@@ -13,7 +14,7 @@ const r = "direct", o = (e) => e.charAt(0).toUpperCase() + e.slice(1), t = (e) =
13
14
  distributionModel
14
15
  whySpecial
15
16
  }
16
- `, f = {
17
+ `, S = {
17
18
  name: "KvLoanUse",
18
19
  props: {
19
20
  anonymizationLevel: {
@@ -42,7 +43,7 @@ const r = "direct", o = (e) => e.charAt(0).toUpperCase() + e.slice(1), t = (e) =
42
43
  },
43
44
  distributionModel: {
44
45
  type: String,
45
- default: r
46
+ default: o
46
47
  },
47
48
  whySpecial: {
48
49
  type: String,
@@ -76,14 +77,33 @@ const r = "direct", o = (e) => e.charAt(0).toUpperCase() + e.slice(1), t = (e) =
76
77
  truncateWordsNumber: {
77
78
  type: Number,
78
79
  default: 0
80
+ },
81
+ /**
82
+ * Opt-in line cap. Unset, the statement keeps the plain 4-line CSS clamp and none of the fit
83
+ * logic runs. Set, the clamp follows this number and, with `showReadMore`, a statement that runs
84
+ * past the cap is trimmed word by word after render so "… read more" ends the last visible line;
85
+ * one that fills the cap on its own is shown whole without the link.
86
+ */
87
+ maxLines: {
88
+ type: Number,
89
+ default: null,
90
+ validator: (e) => e === null || e > 0
91
+ // 0 would hide the whole statement
79
92
  }
80
93
  },
81
94
  computed: {
95
+ hasMaxLines() {
96
+ return this.maxLines !== null;
97
+ },
98
+ // The fit only applies to an opted-in cap with a link to fit; hideBorrowerDetails never renders one.
99
+ fitsReadMore() {
100
+ return this.hasMaxLines && this.showReadMore && !this.hideBorrowerDetails;
101
+ },
82
102
  helpLanguage() {
83
103
  return this.status === "fundraising" || this.status === "inactive" || this.status === "reviewed" ? "helps" : "helped";
84
104
  },
85
105
  isDirect() {
86
- return this.distributionModel === r;
106
+ return this.distributionModel === o;
87
107
  },
88
108
  whySpecialSentence() {
89
109
  return this.whySpecial ? ` This loan is special because ${t(this.whySpecial)}` : "";
@@ -95,19 +115,49 @@ const r = "direct", o = (e) => e.charAt(0).toUpperCase() + e.slice(1), t = (e) =
95
115
  if (this.anonymizationLevel === "full" || this.use.length === 0)
96
116
  return "For the borrower's privacy, this loan has been made anonymous.";
97
117
  if (this.hideBorrowerDetails)
98
- return `${o(this.helpLanguage)} ${t(this.use)}${this.whySpecialSentence}`;
118
+ return `${u(this.helpLanguage)} ${t(this.use)}${this.whySpecialSentence}`;
99
119
  if (this.hideLoanAmount) {
100
120
  const n = this.useIndicativeHelpText ? this.helpLanguage : "Help";
101
- let a = `${o(n)} ${this.nameSpan} ${t(this.use)} ${this.whySpecialSentence}`;
102
- return this.showReadMore && (a = `${i(a, this.truncateWordsNumber)} <span class=" tw-text-action tw-underline">read more</span>`), a;
121
+ let i = `${u(n)} ${this.nameSpan} ${t(this.use)} ${this.whySpecialSentence}`;
122
+ return this.showReadMore && (i = `${a(i, this.truncateWordsNumber)} <span class="${r} tw-text-action tw-underline">read more</span>`), i;
103
123
  }
104
124
  const e = this.borrowerCount > 1;
105
125
  let s = `${l(this.loanAmount).format("$0,0")} ${this.isDirect ? "to" : this.helpLanguage} ${e ? "a member of " : ""}${this.nameSpan} ${this.isDirect ? `${this.helpLanguage} ` : ""}${t(this.use)}${this.whySpecialSentence}`;
106
- return this.showReadMore && (s = `${i(s, this.truncateWordsNumber)} <span class=" tw-text-action tw-underline">read more</span>`), s;
126
+ return this.showReadMore && (s = `${a(s, this.truncateWordsNumber)} <span class="${r} tw-text-action tw-underline">read more</span>`), s;
127
+ }
128
+ },
129
+ watch: {
130
+ loanUse: {
131
+ // Runs after Vue has written the full statement to the DOM but within the same flush, so the
132
+ // fitted text is in place before the browser paints.
133
+ handler() {
134
+ this.fitReadMore();
135
+ },
136
+ flush: "post"
137
+ }
138
+ },
139
+ mounted() {
140
+ if (this.fitsReadMore) {
141
+ if (typeof ResizeObserver > "u") {
142
+ this.fitReadMore();
143
+ return;
144
+ }
145
+ this.resizeObserver = new ResizeObserver(() => this.fitReadMore()), this.resizeObserver.observe(this.$el);
146
+ }
147
+ },
148
+ beforeUnmount() {
149
+ var e;
150
+ (e = this.resizeObserver) == null || e.disconnect();
151
+ },
152
+ methods: {
153
+ fitReadMore() {
154
+ if (!this.fitsReadMore) return;
155
+ const e = this.$el;
156
+ !e || e.clientHeight === 0 || d(e, this.loanUse);
107
157
  }
108
158
  }
109
159
  };
110
160
  export {
111
- m as KV_LOAN_USE_FRAGMENT,
112
- f as default
161
+ w as KV_LOAN_USE_FRAGMENT,
162
+ S as default
113
163
  };
@@ -60,13 +60,22 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
60
60
  type: NumberConstructor;
61
61
  default: number;
62
62
  };
63
+ maxLines: {
64
+ type: NumberConstructor;
65
+ default: any;
66
+ validator: (value: number | null) => boolean;
67
+ };
63
68
  }>, {}, {}, {
69
+ hasMaxLines(): boolean;
70
+ fitsReadMore(): boolean;
64
71
  helpLanguage(): "helps" | "helped";
65
72
  isDirect(): boolean;
66
73
  whySpecialSentence(): string;
67
74
  nameSpan(): string;
68
75
  loanUse(): string;
69
- }, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
76
+ }, {
77
+ fitReadMore(): void;
78
+ }, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
70
79
  anonymizationLevel: {
71
80
  type: StringConstructor;
72
81
  default: string;
@@ -127,12 +136,17 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
127
136
  type: NumberConstructor;
128
137
  default: number;
129
138
  };
139
+ maxLines: {
140
+ type: NumberConstructor;
141
+ default: any;
142
+ validator: (value: number | null) => boolean;
143
+ };
130
144
  }>> & Readonly<{}>, {
131
145
  name: string;
132
146
  use: string;
147
+ status: string;
133
148
  anonymizationLevel: string;
134
149
  loanAmount: string;
135
- status: string;
136
150
  borrowerCount: number;
137
151
  distributionModel: string;
138
152
  whySpecial: string;
@@ -143,5 +157,6 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
143
157
  useIndicativeHelpText: boolean;
144
158
  showReadMore: boolean;
145
159
  truncateWordsNumber: number;
160
+ maxLines: number;
146
161
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
147
162
  export default _default;
@@ -7,6 +7,7 @@ export { default as KvCarousel } from './KvCarousel.vue';
7
7
  export { default as KvCartModal } from './KvCartModal.vue';
8
8
  export { default as KvCartPill } from './KvCartPill.vue';
9
9
  export { default as KvCheckbox } from './KvCheckbox.vue';
10
+ export { default as KvCheckoutLightbox } from './KvCheckoutLightbox.vue';
10
11
  export { default as KvCheckoutReceipt } from './KvCheckoutReceipt.vue';
11
12
  export { default as KvChip } from './KvChip.vue';
12
13
  export { default as KvClassicLoanCard } from './KvClassicLoanCard.vue';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiva/kv-components",
3
- "version": "10.7.0",
3
+ "version": "10.9.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -116,5 +116,5 @@
116
116
  "dependencies": {
117
117
  "fuse.js": "^7.1.0"
118
118
  },
119
- "gitHead": "23f2971b14728c8457a2e3bccd9e57c50fb016d1"
119
+ "gitHead": "32c4cdce12614dadf8fd607ec4df25194d7a7b60"
120
120
  }