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