kemet-ui 1.1.0 → 1.2.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.
@@ -609,10 +609,10 @@ class FormSubmitController {
609
609
  if (!disabled && typeof name === 'string' && typeof value !== 'undefined') {
610
610
  if (Array.isArray(value)) {
611
611
  value.forEach((val) => {
612
- event.formData.append(name, val.toString());
612
+ event.formData?.append(name, val.toString());
613
613
  });
614
614
  } else {
615
- event.formData.append(name, value.toString());
615
+ event.formData?.append(name, value.toString());
616
616
  }
617
617
  }
618
618
  }
@@ -621,6 +621,8 @@ class FormSubmitController {
621
621
  const disabled = this.options.disabled(this.host);
622
622
  const checkValidity = this.options.checkValidity;
623
623
 
624
+ this.form = this.options.form(this.host);
625
+
624
626
  if (this.form && !this.form.noValidate && !disabled && !checkValidity(this.host)) {
625
627
  event.preventDefault();
626
628
  event.stopImmediatePropagation();
@@ -2965,6 +2967,214 @@ class KemetDrawer extends s$1 {
2965
2967
  // eslint-disable-next-line no-unused-expressions
2966
2968
  customElements.get('kemet-drawer') || customElements.define('kemet-drawer', KemetDrawer);
2967
2969
 
2970
+ class KemetFAB extends s$1 {
2971
+ static get styles() {
2972
+ return [
2973
+ r$3`
2974
+ :host {
2975
+ --kemet-fab-size: 50px;
2976
+
2977
+ display: inline-block;
2978
+ position: relative;
2979
+ }
2980
+
2981
+ button {
2982
+ color: var(--kemet-fab-color, var(--kemet-color-white));
2983
+ font-size: inherit;
2984
+ display: inline-flex;
2985
+ padding: 0;
2986
+ position: relative;
2987
+ min-height: var(--kemet-fab-size);
2988
+ min-width: var(--kemet-fab-size);
2989
+ max-width: var(--kemet-fab-size);
2990
+ align-items: center;
2991
+ justify-content: flex-start;
2992
+ transition: all 0.4s ease;
2993
+ border: none;
2994
+ background-color: var(--kemet-fab-background-color, var(--kemet-color-primary));
2995
+ }
2996
+
2997
+ :host([outlined]) button {
2998
+ color: var(--kemet-fab-outlined-color, var(--kemet-color-primary));
2999
+ border: var(--kemet-fab-outlined-border, 1px solid var(--kemet-color-primary));
3000
+ background-color: transparent;
3001
+ }
3002
+
3003
+ :host([pill]) button {
3004
+ border-radius: var(--kemet-fab-pill-radius, 10rem);
3005
+ }
3006
+
3007
+ button::before {
3008
+ content: '';
3009
+ position: absolute;
3010
+ top: 0;
3011
+ left: 0;
3012
+ right: 0;
3013
+ bottom: 0;
3014
+ z-index: -1;
3015
+ width: 100%;
3016
+ height: 100%;
3017
+ transition: background-color 0.3s ease-in-out;
3018
+ background-color: var(--kemet-fab-background-color, var(--kemet-color-primary));
3019
+ }
3020
+
3021
+ :host([pill]) button::before {
3022
+ border-radius: var(--kemet-fab-pill-radius, 10rem);
3023
+ }
3024
+
3025
+ :host([outlined]) button::before {
3026
+ border: var(--kemet-fab-outline-border, 1px solid var(--kemet-color-primary));
3027
+ background-color: transparent;
3028
+ }
3029
+
3030
+ :host([expanded]) button {
3031
+ max-width: 99rem;
3032
+ padding: 0 1.35rem 0 0.25rem;
3033
+ }
3034
+
3035
+ :host([disabled]) button {
3036
+ opacity: 0.5;
3037
+ cursor: not-allowed;
3038
+ }
3039
+
3040
+ .icon {
3041
+ width: var(--kemet-fab-size);
3042
+ height: var(--kemet-fab-size);
3043
+ display: flex;
3044
+ flex: 0 0 auto;
3045
+ align-items: center;
3046
+ justify-content: center;
3047
+ }
3048
+
3049
+ .text {
3050
+ color: var(--kemet-fab-color, var(--kemet-color-white));
3051
+ z-index: 1;
3052
+ white-space: nowrap;
3053
+ pointer-events: none;
3054
+ opacity: 0;
3055
+ transition: opacity 0.2s ease-in-out;
3056
+ }
3057
+
3058
+ :host([outlined]) .text {
3059
+ color: var(--kemet-fab-outlined-color, var(--kemet-color-primary));
3060
+ }
3061
+
3062
+ :host([expanded]) .text {
3063
+ opacity: 1;
3064
+ }
3065
+ `,
3066
+ ];
3067
+ }
3068
+
3069
+ static get properties() {
3070
+ return {
3071
+ /**
3072
+ * The expanded state of the button
3073
+ */
3074
+ expanded: {
3075
+ type: Boolean,
3076
+ reflect: true,
3077
+ },
3078
+ /**
3079
+ * Outline style for a button
3080
+ */
3081
+ outlined: {
3082
+ type: Boolean,
3083
+ reflect: true,
3084
+ },
3085
+ /**
3086
+ * Determines whether not a button is disabled
3087
+ */
3088
+ disabled: {
3089
+ type: Boolean,
3090
+ reflect: true,
3091
+ },
3092
+ /**
3093
+ * Displays the fab with rounded corners
3094
+ */
3095
+ pill: {
3096
+ type: Boolean,
3097
+ reflect: true,
3098
+ },
3099
+ /**
3100
+ * The distance, in pixels, where the fab should automatically expand
3101
+ */
3102
+ expandPoint: {
3103
+ type: Number,
3104
+ attribute: 'expand-point',
3105
+ },
3106
+ /**
3107
+ * The distance, in pixels, where the fab should automatically collapse
3108
+ */
3109
+ collapsePoint: {
3110
+ type: Number,
3111
+ attribute: 'collapse-point',
3112
+ },
3113
+ };
3114
+ }
3115
+
3116
+ constructor() {
3117
+ super();
3118
+
3119
+ this.outlined = false;
3120
+ this.disabled = false;
3121
+ this.expanded = false;
3122
+ }
3123
+
3124
+ firstUpdated() {
3125
+ // events
3126
+ window.addEventListener('scroll', this.handleScroll.bind(this));
3127
+ this.addEventListener('mouseover', this.handleMouseOver.bind(this));
3128
+ this.addEventListener('mouseout', this.handleMouseOut.bind(this));
3129
+ }
3130
+
3131
+ render() {
3132
+ return $`
3133
+ <button class="button" part="button" ?disabled=${this.disabled} aria-disabled=${this.disabled ? 'true' : 'false'}>
3134
+ <span class="icon" part="icon">
3135
+ <slot name="icon"></slot>
3136
+ </span>
3137
+ <span class="text" part="text">
3138
+ <slot></slot>
3139
+ </span>
3140
+ </button>
3141
+ `;
3142
+ }
3143
+
3144
+ handleMouseOver() {
3145
+ if (!this.disabled) {
3146
+ this.expanded = true;
3147
+ }
3148
+ }
3149
+
3150
+ handleMouseOut() {
3151
+ if (!this.disabled) {
3152
+ this.expanded = false;
3153
+ }
3154
+ }
3155
+
3156
+ handleScroll() {
3157
+ if (window.scrollY > this.expandPoint) {
3158
+ this.expanded = true;
3159
+ }
3160
+
3161
+ if (window.scrollY < this.expandPoint) {
3162
+ this.expanded = false;
3163
+ }
3164
+
3165
+ if (window.scrollY > this.collapsePoint) {
3166
+ this.expanded = false;
3167
+ }
3168
+
3169
+ if (window.scrollY < this.collapsePoint) {
3170
+ this.expanded = true;
3171
+ }
3172
+ }
3173
+ }
3174
+
3175
+ // eslint-disable-next-line no-unused-expressions
3176
+ customElements.get('kemet-fab') || customElements.define('kemet-fab', KemetFAB);
3177
+
2968
3178
  class KemetField extends s$1 {
2969
3179
  static get styles() {
2970
3180
  return [
@@ -3746,6 +3956,13 @@ class KemetInput extends s$1 {
3746
3956
  inputType: {
3747
3957
  type: String,
3748
3958
  },
3959
+ /**
3960
+ * Determines if the input is focused
3961
+ */
3962
+ focused: {
3963
+ type: Boolean,
3964
+ reflect: true,
3965
+ },
3749
3966
  };
3750
3967
  }
3751
3968
 
@@ -3760,6 +3977,7 @@ class KemetInput extends s$1 {
3760
3977
  this.iconSize = 20;
3761
3978
  this.validateOnBlur = false;
3762
3979
  this.isPasswordVisible = false;
3980
+ this.focused = false;
3763
3981
 
3764
3982
  /**
3765
3983
  * Used only for form reactive controller
@@ -3773,6 +3991,10 @@ class KemetInput extends s$1 {
3773
3991
  this.field = this.closest('kemet-field');
3774
3992
  this.form = this.closest('form');
3775
3993
  this.slug = this.field ? this.field.slug : 'input';
3994
+
3995
+ if (this.field) {
3996
+ this.field.addEventListener('fmc-password-status', event => this.handleStatus(event));
3997
+ }
3776
3998
  }
3777
3999
 
3778
4000
  render() {
@@ -3887,7 +4109,7 @@ class KemetInput extends s$1 {
3887
4109
  * @private
3888
4110
  */
3889
4111
  handleFocus() {
3890
- this.hasFocus = true;
4112
+ this.focused = true;
3891
4113
 
3892
4114
  /**
3893
4115
  * Fires when the input receives and loses focus
@@ -3911,7 +4133,7 @@ class KemetInput extends s$1 {
3911
4133
  this.validity = this.input.validity;
3912
4134
  }
3913
4135
 
3914
- this.hasFocus = false;
4136
+ this.focused = false;
3915
4137
 
3916
4138
  /**
3917
4139
  * Fires when the input receives and loses focus
@@ -3961,8 +4183,7 @@ class KemetInput extends s$1 {
3961
4183
  * Handles when the input receives input
3962
4184
  * @private
3963
4185
  */
3964
- handleInput(e) {
3965
- this.inputType = e.inputType;
4186
+ handleInput() {
3966
4187
  this.value = this.input.value;
3967
4188
 
3968
4189
  /**
@@ -4453,66 +4674,343 @@ class KemetModal extends s$1 {
4453
4674
  this.focusableElements[0].focus();
4454
4675
  }, 100);
4455
4676
 
4456
- /**
4457
- * Fires when the modal opens
4458
- */
4459
- this.dispatchEvent(
4460
- new CustomEvent('kemet-modal-opened', {
4461
- bubbles: true,
4462
- composed: true,
4463
- detail: this,
4464
- }),
4465
- );
4466
- }
4677
+ /**
4678
+ * Fires when the modal opens
4679
+ */
4680
+ this.dispatchEvent(
4681
+ new CustomEvent('kemet-modal-opened', {
4682
+ bubbles: true,
4683
+ composed: true,
4684
+ detail: this,
4685
+ }),
4686
+ );
4687
+ }
4688
+
4689
+ if (prevProps.get('opened') && this.opened === false) {
4690
+ /**
4691
+ * Fires when the modal closes
4692
+ */
4693
+ this.dispatchEvent(
4694
+ new CustomEvent('kemet-modal-closed', {
4695
+ bubbles: true,
4696
+ composed: true,
4697
+ detail: this,
4698
+ }),
4699
+ );
4700
+ }
4701
+
4702
+ this.isMobile();
4703
+ }
4704
+
4705
+ render() {
4706
+ return $`
4707
+ <div class="content" part="content">
4708
+ <slot></slot>
4709
+ </div>
4710
+ <div class="overlay" part="overlay"></div>
4711
+ `;
4712
+ }
4713
+
4714
+ isMobile() {
4715
+ const mediaQuery = window.matchMedia(`(max-width: ${this.breakpoint})`);
4716
+ this.mobile = mediaQuery.matches;
4717
+ }
4718
+
4719
+ handleFocusableDown(event) {
4720
+ const firstFocusable = this.focusableElements[0];
4721
+ const lastFocusable = this.focusableElements[this.focusableElements.length - 1];
4722
+
4723
+ if (event.key === 'Tab') {
4724
+ if (event.shiftKey && document.activeElement === firstFocusable) {
4725
+ event.preventDefault();
4726
+ lastFocusable.focus();
4727
+ } else if (!event.shiftKey && document.activeElement === lastFocusable) {
4728
+ event.preventDefault();
4729
+ firstFocusable.focus();
4730
+ }
4731
+ }
4732
+ }
4733
+ }
4734
+
4735
+ // eslint-disable-next-line no-unused-expressions
4736
+ customElements.get('kemet-modal') || customElements.define('kemet-modal', KemetModal);
4737
+
4738
+ class KemetPassword extends s$1 {
4739
+ static get styles() {
4740
+ return [
4741
+ r$3`
4742
+ :host {
4743
+ color: inherit;
4744
+ display: none;
4745
+ }
4746
+
4747
+ :host([show]) {
4748
+ display: block;
4749
+ }
4750
+
4751
+ p {
4752
+ margin: 1.6rem 0;
4753
+ }
4754
+
4755
+ .status {
4756
+ display: grid;
4757
+ align-items: center;
4758
+ grid-template-columns: 1fr auto;
4759
+ line-height: 1;
4760
+ margin-top: 0.8rem;
4761
+ }
4762
+
4763
+ .status > span {
4764
+ text-transform: capitalize;
4765
+ }
4766
+
4767
+ .status--weak,
4768
+ .status--better {
4769
+ color: var(--kemet-color-error);
4770
+ }
4771
+
4772
+ .status--strong {
4773
+ color: var(--kemet-color-success);
4774
+ }
4775
+
4776
+ .indicator {
4777
+ display: flex;
4778
+ gap: 0.4rem;
4779
+ margin: 0;
4780
+ list-style: none;
4781
+ }
4782
+
4783
+ .indicator li {
4784
+ width: 1.6rem;
4785
+ height: 0.2rem;
4786
+ background-color: var(--fds-color--gray2);
4787
+ }
4788
+
4789
+ .status--weak .indicator li:nth-child(1) {
4790
+ background-color: var(--kemet-color-error);
4791
+ }
4792
+
4793
+ .status--better .indicator li:nth-child(1),
4794
+ .status--better .indicator li:nth-child(2) {
4795
+ background-color: var(--kemet-color-error);
4796
+ }
4797
+
4798
+ .status--strong .indicator li {
4799
+ background-color: var(--kemet-color-success);
4800
+ }
4801
+
4802
+ .rules {
4803
+ color: var(--kemet-color-primary);
4804
+ list-style: none;
4805
+ font-size: 90%;
4806
+ padding-left: 2rem;
4807
+ }
4808
+
4809
+ .rules li {
4810
+ position: relative;
4811
+ }
4812
+
4813
+ .rules kemet-icon {
4814
+ color: var(--kemet-color-success);
4815
+ position: absolute;
4816
+ left: -2.4rem;
4817
+ }
4818
+ `,
4819
+ ];
4820
+ }
4821
+
4822
+ static get properties() {
4823
+ return {
4824
+ /**
4825
+ * An array of objects containing the rules the password must meet
4826
+ */
4827
+ rules: {
4828
+ type: Array,
4829
+ },
4830
+ /**
4831
+ * Controls the display of the component
4832
+ */
4833
+ show: {
4834
+ type: Boolean,
4835
+ reflect: true,
4836
+ },
4837
+ /**
4838
+ * The value of the input component
4839
+ */
4840
+ value: {
4841
+ type: String,
4842
+ },
4843
+ /**
4844
+ * A message that is above the rules
4845
+ */
4846
+ message: {
4847
+ type: String,
4848
+ },
4849
+ /**
4850
+ * The strength of the password. Weak | Better | Strong
4851
+ */
4852
+ strength: {
4853
+ type: String,
4854
+ },
4855
+ icon: {
4856
+ type: String,
4857
+ },
4858
+ iconSize: {
4859
+ type: Number,
4860
+ },
4861
+ };
4862
+ }
4863
+
4864
+ constructor() {
4865
+ super();
4866
+
4867
+ // managed properties
4868
+ this.rules = [
4869
+ { pattern: '(?=.{8,}$)', message: 'At least 8 characters long' },
4870
+ { pattern: '(?=.*[a-z])(?=.*[A-Z])', message: 'Uppercase and lowercase' },
4871
+ { pattern: '(?=.*[0-9])', message: 'At least one number (0-9)' },
4872
+ ];
4873
+ this.message = 'Please make sure you meet all the requirements.';
4874
+ this.icon = 'check2';
4875
+ this.iconSize = 16;
4876
+ }
4877
+
4878
+ firstUpdated() {
4879
+ // standard properties
4880
+ this.field = this.closest('kemet-field');
4881
+ this.input = this.field.querySelector('[slot="input"]');
4882
+
4883
+ // managed properties
4884
+ this.slug = this.field.slug;
4885
+
4886
+ // events listeners
4887
+ this.input.addEventListener('kemet-input-input', this.handleInput.bind(this));
4888
+ }
4889
+
4890
+ render() {
4891
+ return $`
4892
+ <div role="alert" aria-live="assertive">
4893
+ <div class="status status--${this.strength}" part="status">
4894
+ <span>${this.strength}</span>
4895
+ <ul class="indicator" part="indicator">
4896
+ <li></li>
4897
+ <li></li>
4898
+ <li></li>
4899
+ </ul>
4900
+ </div>
4901
+ <p class="message" part="message">${this.message}</p>
4902
+ <ul class="rules" part="rules">
4903
+ ${this.makeRules()}
4904
+ </ul>
4905
+ </div>
4906
+ `;
4907
+ }
4908
+
4909
+ /**
4910
+ * Makes the list of rules
4911
+ * @private
4912
+ * @returns {templateResults} the criteria rules
4913
+ */
4914
+ makeRules() {
4915
+ if (this.input) {
4916
+ const rules = this.rules.map((rule, index) => {
4917
+ const regExp = new RegExp(rule.pattern);
4918
+ const meetsCriteria = regExp.test(this.input.value);
4919
+ this.rules[index].meetsCriteria = meetsCriteria;
4920
+ return $`<li>${this.makeCheckIcon(meetsCriteria)} ${rule.message}</li>`;
4921
+ });
4922
+
4923
+ return rules;
4924
+ }
4925
+
4926
+ return null;
4927
+ }
4928
+
4929
+ /**
4930
+ * Makes the check icon if the criteria has been met
4931
+ * @param {boolean} meetsCriteria
4932
+ * @private
4933
+ * @returns {templateResults} an icon component
4934
+ */
4935
+ makeCheckIcon(meetsCriteria) {
4936
+ if (meetsCriteria) {
4937
+ return $`<kemet-icon icon=${this.icon} size=${this.iconSize}></kemet-icon>`;
4938
+ }
4939
+
4940
+ return null;
4941
+ }
4942
+
4943
+ /**
4944
+ * Handles the kemet-input-input event
4945
+ * @param {*} event
4946
+ * @private
4947
+ */
4948
+ handleInput(event) {
4949
+ this.value = event.detail;
4950
+ this.setStrength();
4951
+ this.visibility();
4952
+ }
4953
+
4954
+ /**
4955
+ * Determines the strength of the password
4956
+ * @private
4957
+ */
4958
+ setStrength() {
4959
+ // We use a setTimeout here because makeRules adds the meetsCriteria
4960
+ // property to the rules. We need that to be set by render() before
4961
+ // setting the Strength because the percentage calc is based on it
4962
+ setTimeout(() => {
4963
+ const totalRules = this.rules.length;
4964
+ const metRules = this.rules.filter(rule => rule.meetsCriteria).length;
4965
+ const metRulesPercentage = Math.round((metRules / totalRules) * 100) / 100;
4966
+
4967
+ if (metRulesPercentage <= 0.33) {
4968
+ this.strength = 'weak';
4969
+ this.status = 'error';
4970
+ }
4971
+
4972
+ if (metRulesPercentage > 0.33 && metRulesPercentage <= 0.67) {
4973
+ this.strength = 'better';
4974
+ this.status = 'error';
4975
+ }
4976
+
4977
+ if (metRulesPercentage > 0.67) {
4978
+ this.strength = 'strong';
4979
+ this.status = 'success';
4980
+ }
4467
4981
 
4468
- if (prevProps.get('opened') && this.opened === false) {
4469
4982
  /**
4470
- * Fires when the modal closes
4983
+ * Fires when there's a change in status.
4471
4984
  */
4472
4985
  this.dispatchEvent(
4473
- new CustomEvent('kemet-modal-closed', {
4986
+ new CustomEvent('kemet-password-status', {
4474
4987
  bubbles: true,
4475
4988
  composed: true,
4476
- detail: this,
4989
+ detail: {
4990
+ status: this.status,
4991
+ validity: { meetsPasswordCriteria: this.status === 'success' },
4992
+ element: this,
4993
+ },
4477
4994
  }),
4478
4995
  );
4479
- }
4480
-
4481
- this.isMobile();
4482
- }
4483
-
4484
- render() {
4485
- return $`
4486
- <div class="content" part="content">
4487
- <slot></slot>
4488
- </div>
4489
- <div class="overlay" part="overlay"></div>
4490
- `;
4491
- }
4492
-
4493
- isMobile() {
4494
- const mediaQuery = window.matchMedia(`(max-width: ${this.breakpoint})`);
4495
- this.mobile = mediaQuery.matches;
4996
+ }, 1);
4496
4997
  }
4497
4998
 
4498
- handleFocusableDown(event) {
4499
- const firstFocusable = this.focusableElements[0];
4500
- const lastFocusable = this.focusableElements[this.focusableElements.length - 1];
4501
-
4502
- if (event.key === 'Tab') {
4503
- if (event.shiftKey && document.activeElement === firstFocusable) {
4504
- event.preventDefault();
4505
- lastFocusable.focus();
4506
- } else if (!event.shiftKey && document.activeElement === lastFocusable) {
4507
- event.preventDefault();
4508
- firstFocusable.focus();
4509
- }
4999
+ /**
5000
+ * Determines whether to show or hide the component
5001
+ * @private
5002
+ */
5003
+ visibility() {
5004
+ if (this.value.length > 0) {
5005
+ this.show = true;
5006
+ } else {
5007
+ this.show = false;
4510
5008
  }
4511
5009
  }
4512
5010
  }
4513
5011
 
4514
5012
  // eslint-disable-next-line no-unused-expressions
4515
- customElements.get('kemet-modal') || customElements.define('kemet-modal', KemetModal);
5013
+ customElements.get('kemet-password') || customElements.define('kemet-password', KemetPassword);
4516
5014
 
4517
5015
  const keyCodes$1 = {
4518
5016
  ENTER: 13,
@@ -8756,6 +9254,319 @@ class KemetToggle extends s$1 {
8756
9254
  // eslint-disable-next-line no-unused-expressions
8757
9255
  customElements.get('kemet-toggle') || customElements.define('kemet-toggle', KemetToggle);
8758
9256
 
9257
+ class KemetTrackerStep extends s$1 {
9258
+ static get styles() {
9259
+ return [
9260
+ r$3`
9261
+ :host {
9262
+ --kemet-tracker-step-dot-size: 50px;
9263
+ --kemet-tracker-step-dot-size-mobile: 25px;
9264
+ --kemet-tracker-step-dot-gap: 4px;
9265
+ --kemet-tracker-step-standard-line-weight: 1px;
9266
+ --kemet-tracker-step-completed-line-weight: 3px;
9267
+ --kemet-tracker-step-dot-transition: all 300ms ease-in-out;
9268
+
9269
+ position: relative;
9270
+ width: 100%;
9271
+ text-align: center;
9272
+ }
9273
+
9274
+ .dot {
9275
+ font-size: var(--kemet-tracker-step-dot-font-size, 90%);
9276
+ color: var(--kemet-tracker-step-dot-color, var(--kemet-color-primary));
9277
+ display: flex;
9278
+ align-items: center;
9279
+ justify-content: center;
9280
+ margin: 0 auto;
9281
+ position: relative;
9282
+ z-index: 1;
9283
+ width: var(--kemet-tracker-step-dot-size);
9284
+ height: var(--kemet-tracker-step-dot-size);
9285
+ border-radius: 100%;
9286
+ transition: var(--kemet-tracker-step-dot-transition);
9287
+ background-color: var(--kemet-tracker-step-dot-background-color, var(--kemet-color-white));
9288
+ }
9289
+
9290
+ .dot::after {
9291
+ content: "";
9292
+ width: unset;
9293
+ height: unset;
9294
+ position: absolute;
9295
+ top: var(--kemet-tracker-step-dot-gap);
9296
+ left: var(--kemet-tracker-step-dot-gap);
9297
+ right: var(--kemet-tracker-step-dot-gap);
9298
+ bottom: var(--kemet-tracker-step-dot-gap);
9299
+ z-index: -1;
9300
+ border-radius: 100%;
9301
+ transition: var(--kemet-tracker-step-dot-transition);
9302
+ border: var(--kemet-tracker-step-dot-border, 1px solid var(--kemet-color-primary));
9303
+ background-color: var(--kemet-tracker-step-dot-fill-color, transparent);
9304
+ }
9305
+
9306
+ :host([completed]) .dot {
9307
+ color: var(--kemet-tracker-step-completed-color, var(--kemet-color-white));
9308
+ }
9309
+
9310
+ :host([completed]) .dot::after {
9311
+ border: 0;
9312
+ background-color: var(--kemet-tracker-step-completed-fill-color, var(--kemet-color-success));
9313
+ }
9314
+
9315
+ :host([current]) .dot.animate {
9316
+ color: var(--kemet-tracker-step-current-color, var(--kemet-color-white));
9317
+ }
9318
+
9319
+ :host([current]) .dot.animate::after {
9320
+ border: 0;
9321
+ background-color: var(--kemet-tracker-step-current-fill-color, var(--kemet-color-primary));
9322
+ }
9323
+
9324
+ :host([mobile]) .dot {
9325
+ width: var(--kemet-tracker-step-dot-size-mobile);
9326
+ height: var(--kemet-tracker-step-dot-size-mobile);
9327
+ }
9328
+
9329
+ .line {
9330
+ display: block;
9331
+ position: absolute;
9332
+ left: 50%;
9333
+ right: 0;
9334
+ bottom: 0;
9335
+ width: 100%;
9336
+
9337
+ /* standard */
9338
+ top: calc((var(--kemet-tracker-step-dot-size) - var(--kemet-tracker-step-standard-line-weight)) / 2);
9339
+ height: var(--kemet-tracker-step-standard-line-weight);
9340
+ background-color: var(--kemet-tracker-step-line-color, var(--kemet-color-primary));
9341
+ }
9342
+
9343
+ .line.completed {
9344
+ width: 100%;
9345
+ transform: scaleX(0);
9346
+ transform-origin: left center;
9347
+ top: calc((var(--kemet-tracker-step-dot-size) - var(--kemet-tracker-step-completed-line-weight)) / 2);
9348
+ height: var(--kemet-tracker-step-completed-line-weight);
9349
+ background-color: var(--kemet-tracker-step-completed-line-color, var(--kemet-color-primary));
9350
+ animation-name: grow;
9351
+ animation-delay: 1s;
9352
+ animation-duration: 600ms;
9353
+ animation-fill-mode: forwards;
9354
+ }
9355
+
9356
+ :host([mobile]) .line {
9357
+ top: calc((var(--kemet-tracker-step-dot-size-mobile) - var(--kemet-tracker-step-standard-line-weight)) / 2);
9358
+ }
9359
+
9360
+ :host([mobile]) .line.completed {
9361
+ top: calc((var(--kemet-tracker-step-dot-size-mobile) - var(--kemet-tracker-step-completed-line-weight)) / 2);
9362
+ }
9363
+
9364
+ @keyframes grow {
9365
+ from { transform: scaleX(0); }
9366
+ to { transform: scaleX(1); }
9367
+ }
9368
+ `,
9369
+ ];
9370
+ }
9371
+
9372
+ static get properties() {
9373
+ return {
9374
+ /**
9375
+ * The current step number
9376
+ */
9377
+ step: {
9378
+ type: Number,
9379
+ },
9380
+ /**
9381
+ * Determines whether or not a step renders as complete
9382
+ */
9383
+ completed: {
9384
+ type: Boolean,
9385
+ reflect: true,
9386
+ },
9387
+ /**
9388
+ * Determines whether or not a step renders as the current step
9389
+ */
9390
+ current: {
9391
+ type: Boolean,
9392
+ reflect: true,
9393
+ },
9394
+ /**
9395
+ * Is automatically added to the last step
9396
+ */
9397
+ last: {
9398
+ type: Boolean,
9399
+ reflect: true,
9400
+ },
9401
+ /**
9402
+ * Determines if a step should render as mobile
9403
+ */
9404
+ mobile: {
9405
+ type: Boolean,
9406
+ reflect: true,
9407
+ },
9408
+ /**
9409
+ * Hides the label inside of a dot
9410
+ */
9411
+ hideDotContent: {
9412
+ type: Boolean,
9413
+ reflect: true,
9414
+ attribute: 'hide-dot-content',
9415
+ },
9416
+ /**
9417
+ * The icon size for the completed check mark
9418
+ */
9419
+ completedSize: {
9420
+ type: Number,
9421
+ },
9422
+ };
9423
+ }
9424
+
9425
+ constructor() {
9426
+ super();
9427
+ this.completedSize = 16;
9428
+ }
9429
+
9430
+ firstUpdated() {
9431
+ this.tracker = this.closest('kemet-tracker');
9432
+ }
9433
+
9434
+ render() {
9435
+ return $`
9436
+ ${this.makeLine()}
9437
+ ${this.makeCompletedLine()}
9438
+ <div class="dot" part="dot">${this.makeDotContent()}</div>
9439
+ ${this.makeSlotContent()}
9440
+ `;
9441
+ }
9442
+
9443
+ makeLine() {
9444
+ if (!this.last) {
9445
+ return $`<div class="line" part="line"></div>`;
9446
+ }
9447
+
9448
+ return null;
9449
+ }
9450
+
9451
+ makeCompletedLine() {
9452
+ if (this.completed && !this.last) {
9453
+ return $`<div class="line completed" part="completed-line" @animationend=${() => this.handleCompletedLineEnd()}></div>`;
9454
+ }
9455
+
9456
+ return null;
9457
+ }
9458
+
9459
+ makeDotContent() {
9460
+ if (this.completed && !this.mobile && !this.hideDotContent) {
9461
+ return $`
9462
+ <kemet-icon icon="check2" size=${this.completedSize}></kemet-icon>
9463
+ `;
9464
+ }
9465
+
9466
+ if (!this.mobile && !this.hideDotContent) {
9467
+ return $`
9468
+ <span>${this.step}/${this.tracker?.total}</span>
9469
+ `;
9470
+ }
9471
+
9472
+ return null;
9473
+ }
9474
+
9475
+ makeSlotContent() {
9476
+ if (!this.mobile) {
9477
+ return $`<slot></slot>`;
9478
+ }
9479
+
9480
+ return null;
9481
+ }
9482
+
9483
+ handleCompletedLineEnd() {
9484
+ const currentDot = this.tracker.querySelector('[current]').shadowRoot.querySelector('.dot');
9485
+
9486
+ if (currentDot) {
9487
+ currentDot.classList.add('animate');
9488
+ }
9489
+ }
9490
+ }
9491
+
9492
+ // eslint-disable-next-line no-unused-expressions
9493
+ customElements.get('kemet-tracker-step') || customElements.define('kemet-tracker-step', KemetTrackerStep);
9494
+
9495
+ class KemetTracker extends s$1 {
9496
+ static get styles() {
9497
+ return [
9498
+ r$3`
9499
+ :host {
9500
+ display: flex;
9501
+ justify-content: center;
9502
+ align-items: flex-start;
9503
+ }
9504
+ `,
9505
+ ];
9506
+ }
9507
+
9508
+ static get properties() {
9509
+ return {
9510
+ /**
9511
+ * The total number of steps
9512
+ */
9513
+ total: {
9514
+ type: Number,
9515
+ },
9516
+ /**
9517
+ * The point at which the tracker goes from mobile to standard
9518
+ */
9519
+ breakpoint: {
9520
+ type: String,
9521
+ },
9522
+ };
9523
+ }
9524
+
9525
+ constructor() {
9526
+ super();
9527
+ this.breakpoint = '767px';
9528
+ }
9529
+
9530
+ firstUpdated() {
9531
+ // elements
9532
+ this.steps = this.querySelectorAll('kemet-tracker-step');
9533
+
9534
+ // methods exe
9535
+ this.isMobile();
9536
+
9537
+ // events
9538
+ window.addEventListener('resize', () => {
9539
+ this.isMobile();
9540
+ });
9541
+ }
9542
+
9543
+ render() {
9544
+ return $`<slot @slotchange=${() => this.handleSlotChange()}></slot>`;
9545
+ }
9546
+
9547
+ handleSlotChange() {
9548
+ this.total = this.steps.length;
9549
+
9550
+ this.steps.forEach((step, index) => {
9551
+ step.step = index + 1;
9552
+ if (step.step === this.total) {
9553
+ step.last = true;
9554
+ }
9555
+ });
9556
+ }
9557
+
9558
+ isMobile() {
9559
+ const mediaQuery = window.matchMedia(`(max-width: ${this.breakpoint})`);
9560
+
9561
+ this.steps.forEach((step) => {
9562
+ step.mobile = mediaQuery.matches;
9563
+ });
9564
+ }
9565
+ }
9566
+
9567
+ // eslint-disable-next-line no-unused-expressions
9568
+ customElements.get('kemet-tracker') || customElements.define('kemet-tracker', KemetTracker);
9569
+
8759
9570
  exports.KemetCarouselCurrent = KemetCarouselCurrent;
8760
9571
  exports.KemetCarouselFirst = KemetCarouselFirst;
8761
9572
  exports.KemetCarouselLast = KemetCarouselLast;