@formata/limitr-ui 0.1.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.
package/dist/table.js ADDED
@@ -0,0 +1,742 @@
1
+ //
2
+ // Copyright 2026 Formata, Inc. All rights reserved.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ //
16
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
17
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
19
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ };
22
+ var __metadata = (this && this.__metadata) || function (k, v) {
23
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
24
+ };
25
+ import { customElement, property, state } from "lit/decorators.js";
26
+ import { LimitrElement } from "./element.js";
27
+ import { css, html } from "lit";
28
+ /**
29
+ * Pricing table for a Limitr policy, showing all visible plans for a user
30
+ * to choose from (allows them to pick/switch plans).
31
+ */
32
+ let LimitrPricingTable = class LimitrPricingTable extends LimitrElement {
33
+ constructor() {
34
+ super(...arguments);
35
+ this.interactive = true;
36
+ this.denyPolicyChanges = false;
37
+ this.theme = 'light';
38
+ this.currentPlan = null;
39
+ this.loading = true;
40
+ this.showCouponModal = false;
41
+ this.selectedPlanForCoupon = '';
42
+ this.couponCode = '';
43
+ this.couponError = '';
44
+ }
45
+ /**
46
+ * Styles.
47
+ */
48
+ static get styles() {
49
+ return css `
50
+ :host {
51
+ display: block;
52
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
53
+ --limitr-bg-primary: #ffffff;
54
+ --limitr-bg-secondary: #f8f9fa;
55
+ --limitr-bg-hover: #f1f3f5;
56
+ --limitr-text-primary: #000000;
57
+ --limitr-text-secondary: #6c757d;
58
+ --limitr-border: #dee2e6;
59
+ --limitr-accent: #000000;
60
+ --limitr-accent-text: #ffffff;
61
+ --limitr-radius: 12px;
62
+ --limitr-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
63
+ --limitr-shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.12);
64
+ }
65
+
66
+ :host([theme="dark"]) {
67
+ --limitr-bg-primary: #1a1a1a;
68
+ --limitr-bg-secondary: #2d2d2d;
69
+ --limitr-bg-hover: #3a3a3a;
70
+ --limitr-text-primary: #ffffff;
71
+ --limitr-text-secondary: #a0a0a0;
72
+ --limitr-border: #404040;
73
+ --limitr-accent: #ffffff;
74
+ --limitr-accent-text: #000000;
75
+ }
76
+
77
+ .pricing-container {
78
+ display: grid;
79
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
80
+ gap: 24px;
81
+ padding: 24px;
82
+ max-width: 1200px;
83
+ margin: 0 auto;
84
+ }
85
+
86
+ .plan-card {
87
+ background: var(--limitr-bg-primary);
88
+ border: 2px solid var(--limitr-border);
89
+ border-radius: var(--limitr-radius);
90
+ padding: 32px 24px;
91
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
92
+ box-shadow: var(--limitr-shadow);
93
+ position: relative;
94
+ display: flex;
95
+ flex-direction: column;
96
+ }
97
+
98
+ .plan-card.interactive:hover {
99
+ transform: translateY(-4px);
100
+ box-shadow: var(--limitr-shadow-hover);
101
+ border-color: var(--limitr-accent);
102
+ }
103
+
104
+ .plan-card.current {
105
+ border-color: var(--limitr-accent);
106
+ border-width: 2px;
107
+ }
108
+
109
+ .current-badge {
110
+ position: absolute;
111
+ top: 16px;
112
+ right: 16px;
113
+ background: var(--limitr-accent);
114
+ color: var(--limitr-accent-text);
115
+ padding: 4px 12px;
116
+ border-radius: 16px;
117
+ font-size: 12px;
118
+ font-weight: 600;
119
+ text-transform: uppercase;
120
+ letter-spacing: 0.5px;
121
+ }
122
+
123
+ .plan-header {
124
+ margin-bottom: 24px;
125
+ }
126
+
127
+ .plan-name {
128
+ font-size: 24px;
129
+ font-weight: 700;
130
+ color: var(--limitr-text-primary);
131
+ margin: 0 0 8px 0;
132
+ }
133
+
134
+ .plan-price {
135
+ display: flex;
136
+ align-items: baseline;
137
+ gap: 4px;
138
+ margin: 16px 0;
139
+ }
140
+
141
+ .price-prefix {
142
+ font-size: 18px;
143
+ color: var(--limitr-text-secondary);
144
+ font-weight: 500;
145
+ }
146
+
147
+ .price-amount {
148
+ font-size: 48px;
149
+ font-weight: 800;
150
+ color: var(--limitr-text-primary);
151
+ line-height: 1;
152
+ }
153
+
154
+ .price-suffix {
155
+ font-size: 16px;
156
+ color: var(--limitr-text-secondary);
157
+ font-weight: 500;
158
+ }
159
+
160
+ .plan-features {
161
+ list-style: none;
162
+ padding: 0;
163
+ margin: 0 0 32px 0;
164
+ flex-grow: 1;
165
+ }
166
+
167
+ .feature-item {
168
+ padding: 16px 12px;
169
+ border-bottom: 1px solid var(--limitr-border);
170
+ display: flex;
171
+ justify-content: space-between;
172
+ align-items: center;
173
+ gap: 16px;
174
+ }
175
+
176
+ .feature-item:first-child {
177
+ padding-top: 0;
178
+ }
179
+
180
+ .feature-item:last-child {
181
+ border-bottom: none;
182
+ padding-bottom: 0;
183
+ }
184
+
185
+ .feature-name {
186
+ font-size: 15px;
187
+ color: var(--limitr-text-primary);
188
+ font-weight: 500;
189
+ flex: 1;
190
+ line-height: 1.5;
191
+ }
192
+
193
+ .feature-limit {
194
+ font-size: 14px;
195
+ color: var(--limitr-text-secondary);
196
+ font-weight: 600;
197
+ white-space: nowrap;
198
+ }
199
+
200
+ .select-button {
201
+ width: 100%;
202
+ padding: 16px;
203
+ border: 2px solid var(--limitr-accent);
204
+ background: transparent;
205
+ color: var(--limitr-accent);
206
+ font-size: 16px;
207
+ font-weight: 600;
208
+ border-radius: 8px;
209
+ cursor: pointer;
210
+ transition: all 0.2s ease;
211
+ text-transform: uppercase;
212
+ letter-spacing: 0.5px;
213
+ }
214
+
215
+ .select-button:hover {
216
+ background: var(--limitr-accent);
217
+ color: var(--limitr-accent-text);
218
+ }
219
+
220
+ .select-button.current {
221
+ background: var(--limitr-accent);
222
+ color: var(--limitr-accent-text);
223
+ cursor: default;
224
+ }
225
+
226
+ .select-button:disabled {
227
+ opacity: 0.5;
228
+ cursor: not-allowed;
229
+ }
230
+
231
+ .loading {
232
+ text-align: center;
233
+ padding: 48px;
234
+ color: var(--limitr-text-secondary);
235
+ font-size: 16px;
236
+ }
237
+
238
+ .empty {
239
+ text-align: center;
240
+ padding: 48px;
241
+ color: var(--limitr-text-secondary);
242
+ }
243
+
244
+ .empty-title {
245
+ font-size: 20px;
246
+ font-weight: 600;
247
+ margin-bottom: 8px;
248
+ }
249
+
250
+ .coupon-modal-overlay {
251
+ position: fixed;
252
+ top: 0;
253
+ left: 0;
254
+ right: 0;
255
+ bottom: 0;
256
+ background: rgba(0, 0, 0, 0.5);
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ z-index: 2000;
261
+ padding: 24px;
262
+ }
263
+
264
+ .coupon-modal {
265
+ background: var(--limitr-bg-primary);
266
+ border-radius: var(--limitr-radius);
267
+ max-width: 480px;
268
+ width: 100%;
269
+ padding: 32px;
270
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
271
+ }
272
+
273
+ .coupon-modal-title {
274
+ font-size: 24px;
275
+ font-weight: 700;
276
+ color: var(--limitr-text-primary);
277
+ margin: 0 0 8px 0;
278
+ }
279
+
280
+ .coupon-modal-description {
281
+ font-size: 14px;
282
+ color: var(--limitr-text-secondary);
283
+ margin-bottom: 24px;
284
+ line-height: 1.5;
285
+ }
286
+
287
+ .coupon-input-wrapper {
288
+ margin-bottom: 24px;
289
+ }
290
+
291
+ .coupon-input-label {
292
+ display: block;
293
+ font-size: 14px;
294
+ font-weight: 600;
295
+ color: var(--limitr-text-primary);
296
+ margin-bottom: 8px;
297
+ }
298
+
299
+ .coupon-input {
300
+ width: 100%;
301
+ padding: 12px 16px;
302
+ border: 2px solid var(--limitr-border);
303
+ border-radius: 8px;
304
+ font-size: 16px;
305
+ background: var(--limitr-bg-primary);
306
+ color: var(--limitr-text-primary);
307
+ font-family: inherit;
308
+ transition: border-color 0.2s ease;
309
+ box-sizing: border-box;
310
+ }
311
+
312
+ .coupon-input:focus {
313
+ outline: none;
314
+ border-color: var(--limitr-accent);
315
+ }
316
+
317
+ .coupon-input.error {
318
+ border-color: #ef4444;
319
+ }
320
+
321
+ .coupon-error {
322
+ color: #ef4444;
323
+ font-size: 13px;
324
+ margin-top: 6px;
325
+ }
326
+
327
+ .coupon-modal-actions {
328
+ display: flex;
329
+ gap: 12px;
330
+ flex-wrap: wrap;
331
+ }
332
+
333
+ .coupon-button {
334
+ flex: 1;
335
+ min-width: 120px;
336
+ padding: 12px 24px;
337
+ border: 2px solid var(--limitr-accent);
338
+ border-radius: 8px;
339
+ font-size: 14px;
340
+ font-weight: 600;
341
+ cursor: pointer;
342
+ transition: all 0.2s ease;
343
+ text-transform: uppercase;
344
+ letter-spacing: 0.5px;
345
+ }
346
+
347
+ .coupon-button-primary {
348
+ background: var(--limitr-accent);
349
+ color: var(--limitr-accent-text);
350
+ }
351
+
352
+ .coupon-button-primary:hover {
353
+ opacity: 0.9;
354
+ }
355
+
356
+ .coupon-button-secondary {
357
+ background: transparent;
358
+ color: var(--limitr-accent);
359
+ }
360
+
361
+ .coupon-button-secondary:hover {
362
+ background: var(--limitr-bg-secondary);
363
+ }
364
+
365
+ .coupon-button-tertiary {
366
+ background: transparent;
367
+ border-color: var(--limitr-border);
368
+ color: var(--limitr-text-secondary);
369
+ }
370
+
371
+ .coupon-button-tertiary:hover {
372
+ background: var(--limitr-bg-secondary);
373
+ }
374
+
375
+ @media (max-width: 768px) {
376
+ .pricing-container {
377
+ grid-template-columns: 1fr;
378
+ }
379
+
380
+ .coupon-modal-actions {
381
+ flex-direction: column;
382
+ }
383
+
384
+ .coupon-button {
385
+ width: 100%;
386
+ }
387
+ }
388
+ `;
389
+ }
390
+ async connectedCallback() {
391
+ super.connectedCallback();
392
+ await this.loadCurrentPlan();
393
+ }
394
+ async updated(changedProperties) {
395
+ await super.updated(changedProperties);
396
+ if (changedProperties.has('policy') || changedProperties.has('customerId')) {
397
+ await this.loadCurrentPlan();
398
+ }
399
+ }
400
+ async loadCurrentPlan() {
401
+ this.loading = true;
402
+ try {
403
+ this.currentPlan = await this.getCustomerPlan();
404
+ }
405
+ catch (e) {
406
+ console.error('Error loading customer plan:', e);
407
+ this.currentPlan = null;
408
+ }
409
+ finally {
410
+ this.loading = false;
411
+ }
412
+ }
413
+ async handlePlanSelect(planName) {
414
+ if (!this.interactive)
415
+ return;
416
+ // Get the selected plan details
417
+ const selectedPlan = this.getPlan(planName);
418
+ const hasPaidPrice = selectedPlan?.price && selectedPlan.price.amount > 0;
419
+ // Check if plan allows coupons and show coupon modal if so
420
+ const allowsCoupons = selectedPlan?.price?.allowCoupons === true;
421
+ if (hasPaidPrice && allowsCoupons) {
422
+ // Show coupon modal before proceeding
423
+ this.selectedPlanForCoupon = planName;
424
+ this.couponCode = '';
425
+ this.couponError = '';
426
+ this.showCouponModal = true;
427
+ return;
428
+ }
429
+ // Proceed with plan selection (no coupon)
430
+ await this.completePlanSelection(planName, '');
431
+ }
432
+ async completePlanSelection(planName, couponCode) {
433
+ // Get the selected plan details
434
+ const selectedPlan = this.getPlan(planName);
435
+ const hasPaidPrice = selectedPlan?.price && selectedPlan.price.amount > 0;
436
+ // Check if customer has payment method on file
437
+ const customer = await this.customer();
438
+ const hasPaymentMethod = customer?.metadata?.stripe_payment_method_type;
439
+ // If selecting a paid plan without payment method, redirect to Stripe portal
440
+ if (hasPaidPrice && !hasPaymentMethod && this.stripePortalUrl) {
441
+ if (confirm("This plan requires a payment method. You'll be redirected to add your payment details.")) {
442
+ globalThis.location.href = this.stripePortalUrl;
443
+ }
444
+ return;
445
+ }
446
+ // Set coupon code in customer metadata if provided
447
+ if (couponCode && this.policy) {
448
+ customer.metadata = {
449
+ ...customer.metadata,
450
+ stripe_coupon_code: couponCode,
451
+ stripe_coupon_status: 'pending',
452
+ };
453
+ // Send customer update with coupon to server
454
+ await this.policy.setCustomer(customer);
455
+ }
456
+ if (!this.denyPolicyChanges && this.policy && confirm("Are you sure you'd like to change plans? This may result in additional charges to your account.")) {
457
+ // if the new plan was not set on the policy, return without emitting an event
458
+ // overwrite_meters is false so that current period usage is kept in-tact
459
+ // any new meters created will have a starting date equal to the latest active meter starting date from prior plan
460
+ if (!await this.policy.setCustomerPlan(this.customerId, planName, false))
461
+ return;
462
+ }
463
+ const event = new CustomEvent('plan-select', {
464
+ detail: { planName, couponCode },
465
+ bubbles: true,
466
+ composed: true,
467
+ });
468
+ this.dispatchEvent(event);
469
+ await this.loadCurrentPlan();
470
+ this.requestUpdate();
471
+ }
472
+ async handleCouponSubmit() {
473
+ // Basic validation
474
+ if (!this.couponCode || this.couponCode.trim() === '' || !this.customerId) {
475
+ this.couponError = 'Please enter a coupon code';
476
+ return;
477
+ }
478
+ // Make sure the coupon is valid before applying it
479
+ const coupon = this.couponCode.trim();
480
+ const response = await fetch(`https://api.limitr.dev/v1/stripe-ui/coupon-check?coupon=${coupon}&customerId=${this.customerId}`);
481
+ if (response.ok) {
482
+ const json = await response.json();
483
+ if (json.valid) {
484
+ this.showCouponModal = false;
485
+ this.completePlanSelection(this.selectedPlanForCoupon, this.couponCode.trim());
486
+ }
487
+ else {
488
+ this.couponError = 'Not a valid coupon code';
489
+ return;
490
+ }
491
+ }
492
+ else {
493
+ this.couponError = 'Not a valid coupon code';
494
+ return;
495
+ }
496
+ }
497
+ handleCouponSkip() {
498
+ // Close modal and proceed without coupon
499
+ this.showCouponModal = false;
500
+ this.completePlanSelection(this.selectedPlanForCoupon, '');
501
+ }
502
+ handleCouponCancel() {
503
+ // Close modal and don't change plans
504
+ this.showCouponModal = false;
505
+ this.selectedPlanForCoupon = '';
506
+ this.couponCode = '';
507
+ this.couponError = '';
508
+ }
509
+ //deno-lint-ignore no-explicit-any
510
+ formatPrice(price) {
511
+ if (!price)
512
+ return null;
513
+ const amount = typeof price.amount === 'number'
514
+ ? price.amount.toFixed(2)
515
+ : price.amount;
516
+ return {
517
+ prefix: price.prefix || '',
518
+ amount,
519
+ suffix: price.suffix || '',
520
+ };
521
+ }
522
+ /**
523
+ * Pluralize a unit name if needed based on the value.
524
+ */
525
+ pluralizeUnit(unit, value) {
526
+ const numValue = typeof value === 'string' ? parseFloat(value) : value;
527
+ // Don't pluralize if value is 1
528
+ if (numValue === 1)
529
+ return unit;
530
+ // Units that don't need pluralization
531
+ const nonPluralUnits = [
532
+ 'GB', 'MB', 'KB', 'TB', 'MiB', 'GiB', 'KiB', 'TiB',
533
+ 'ms', 'seconds', 'minutes', 'hours', 'days'
534
+ ];
535
+ if (nonPluralUnits.includes(unit)) {
536
+ return unit;
537
+ }
538
+ // Common irregular plurals
539
+ const irregularPlurals = {
540
+ 'seat': 'seats',
541
+ 'token': 'tokens',
542
+ 'request': 'requests',
543
+ 'call': 'calls',
544
+ 'query': 'queries',
545
+ 'credit': 'credits',
546
+ 'user': 'users',
547
+ 'member': 'members',
548
+ 'item': 'items'
549
+ };
550
+ if (irregularPlurals[unit.toLowerCase()]) {
551
+ return irregularPlurals[unit.toLowerCase()];
552
+ }
553
+ // Default: add 's' for pluralization
554
+ if (!unit.endsWith('s')) {
555
+ return unit + 's';
556
+ }
557
+ return unit;
558
+ }
559
+ //deno-lint-ignore no-explicit-any
560
+ formatLimitValue(limit, creditName) {
561
+ if (!limit || limit.value === undefined)
562
+ return 'Unlimited';
563
+ const credit = this.getCredit(creditName);
564
+ const value = limit.value;
565
+ if (credit && credit.stof_units && credit.stof_units !== 'float' && credit.stof_units !== 'int') {
566
+ return `${value} ${credit.stof_units}`;
567
+ }
568
+ if (credit && credit.unit) {
569
+ const pluralizedUnit = this.pluralizeUnit(credit.unit, value);
570
+ return `${value} ${pluralizedUnit}`;
571
+ }
572
+ return `${value}`;
573
+ }
574
+ //deno-lint-ignore no-explicit-any
575
+ renderPlanCard(plan, isCurrent) {
576
+ const price = this.formatPrice(plan.price);
577
+ const entitlements = plan.entitlements || {};
578
+ // Filter out hidden entitlements
579
+ //deno-lint-ignore no-explicit-any
580
+ const visibleEntitlements = Object.entries(entitlements).filter(([_, ent]) => !ent.hidden);
581
+ return html `
582
+ <div class="plan-card ${this.interactive ? 'interactive' : ''} ${isCurrent ? 'current' : ''}">
583
+ ${isCurrent ? html `<div class="current-badge">Current Plan</div>` : ''}
584
+
585
+ <div class="plan-header">
586
+ <h3 class="plan-name">${plan.label || plan.name}</h3>
587
+
588
+ ${price ? html `
589
+ <div class="plan-price">
590
+ ${price.prefix ? html `<span class="price-prefix">${price.prefix}</span>` : ''}
591
+ <span class="price-amount">${price.amount}</span>
592
+ ${price.suffix ? html `<span class="price-suffix">${price.suffix}</span>` : ''}
593
+ </div>
594
+ ` : ''}
595
+ </div>
596
+
597
+ <ul class="plan-features">
598
+ ${
599
+ //deno-lint-ignore no-explicit-any
600
+ visibleEntitlements.map(([entName, entitlement]) => {
601
+ const limit = entitlement.limit;
602
+ return html `
603
+ <li class="feature-item">
604
+ <span class="feature-name">${entitlement.description || entName}</span>
605
+ ${limit ? html `
606
+ <span class="feature-limit">${this.formatLimitValue(limit, limit.credit)}</span>
607
+ ` : html `
608
+ <span class="feature-limit">✓</span>
609
+ `}
610
+ </li>
611
+ `;
612
+ })}
613
+ </ul>
614
+
615
+ ${this.interactive ? html `
616
+ <button
617
+ class="select-button ${isCurrent ? 'current' : ''}"
618
+ ?disabled=${isCurrent}
619
+ @click=${() => this.handlePlanSelect(plan.name)}
620
+ >
621
+ ${isCurrent ? 'Current Plan' : 'Select Plan'}
622
+ </button>
623
+ ` : ''}
624
+ </div>
625
+ `;
626
+ }
627
+ /**
628
+ * Render.
629
+ */
630
+ render() {
631
+ if (this.loading) {
632
+ return html `<div class="loading">Loading plans...</div>`;
633
+ }
634
+ const plans = this.visiblePlans;
635
+ if (plans.length === 0) {
636
+ return html `
637
+ <div class="empty">
638
+ <div class="empty-title">No plans available</div>
639
+ </div>
640
+ `;
641
+ }
642
+ return html `
643
+ <div class="pricing-container">
644
+ ${plans.map(plan => this.renderPlanCard(plan, this.currentPlan?.name === plan.name))}
645
+ </div>
646
+
647
+ ${this.showCouponModal ? html `
648
+ <div class="coupon-modal-overlay" @click=${this.handleCouponCancel}>
649
+ <div class="coupon-modal" @click=${(e) => e.stopPropagation()}>
650
+ <h2 class="coupon-modal-title">Have a Coupon Code?</h2>
651
+ <p class="coupon-modal-description">
652
+ Enter your coupon code to apply a discount to your subscription.
653
+ </p>
654
+
655
+ <div class="coupon-input-wrapper">
656
+ <label class="coupon-input-label" for="coupon-input">
657
+ Coupon Code
658
+ </label>
659
+ <input
660
+ id="coupon-input"
661
+ type="text"
662
+ class="coupon-input ${this.couponError ? 'error' : ''}"
663
+ .value=${this.couponCode}
664
+ @input=${(e) => {
665
+ this.couponCode = e.target.value;
666
+ this.couponError = '';
667
+ }}
668
+ @keypress=${(e) => {
669
+ if (e.key === 'Enter') {
670
+ this.handleCouponSubmit();
671
+ }
672
+ }}
673
+ placeholder="Enter code"
674
+ autocomplete="off"
675
+ />
676
+ ${this.couponError ? html `
677
+ <div class="coupon-error">${this.couponError}</div>
678
+ ` : ''}
679
+ </div>
680
+
681
+ <div class="coupon-modal-actions">
682
+ <button class="coupon-button coupon-button-primary" @click=${this.handleCouponSubmit}>
683
+ Apply Coupon
684
+ </button>
685
+ <button class="coupon-button coupon-button-secondary" @click=${this.handleCouponSkip}>
686
+ Skip
687
+ </button>
688
+ <button class="coupon-button coupon-button-tertiary" @click=${this.handleCouponCancel}>
689
+ Cancel
690
+ </button>
691
+ </div>
692
+ </div>
693
+ </div>
694
+ ` : ''}
695
+ `;
696
+ }
697
+ };
698
+ __decorate([
699
+ property({ type: Boolean }),
700
+ __metadata("design:type", Boolean)
701
+ ], LimitrPricingTable.prototype, "interactive", void 0);
702
+ __decorate([
703
+ property({ type: Boolean })
704
+ /** When true, emit plan select events only, without setting customer plan on policy. */
705
+ ,
706
+ __metadata("design:type", Boolean)
707
+ ], LimitrPricingTable.prototype, "denyPolicyChanges", void 0);
708
+ __decorate([
709
+ property(),
710
+ __metadata("design:type", String)
711
+ ], LimitrPricingTable.prototype, "theme", void 0);
712
+ __decorate([
713
+ state()
714
+ //deno-lint-ignore no-explicit-any
715
+ ,
716
+ __metadata("design:type", Object)
717
+ ], LimitrPricingTable.prototype, "currentPlan", void 0);
718
+ __decorate([
719
+ state(),
720
+ __metadata("design:type", Boolean)
721
+ ], LimitrPricingTable.prototype, "loading", void 0);
722
+ __decorate([
723
+ state(),
724
+ __metadata("design:type", Boolean)
725
+ ], LimitrPricingTable.prototype, "showCouponModal", void 0);
726
+ __decorate([
727
+ state(),
728
+ __metadata("design:type", String)
729
+ ], LimitrPricingTable.prototype, "selectedPlanForCoupon", void 0);
730
+ __decorate([
731
+ state(),
732
+ __metadata("design:type", String)
733
+ ], LimitrPricingTable.prototype, "couponCode", void 0);
734
+ __decorate([
735
+ state(),
736
+ __metadata("design:type", String)
737
+ ], LimitrPricingTable.prototype, "couponError", void 0);
738
+ LimitrPricingTable = __decorate([
739
+ customElement('limitr-pricing-table')
740
+ ], LimitrPricingTable);
741
+ export { LimitrPricingTable };
742
+ //# sourceMappingURL=table.js.map