@juspay/svelte-ui-components 2.52.0 → 2.54.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.
@@ -1,15 +1,47 @@
1
1
  <script lang="ts">
2
2
  import type { BadgeProperties } from './properties';
3
3
 
4
- let { image, value, classes }: BadgeProperties = $props();
4
+ let {
5
+ image,
6
+ alt = '',
7
+ value,
8
+ mode = 'count',
9
+ hidden = false,
10
+ ariaLabel,
11
+ testId,
12
+ classes
13
+ }: BadgeProperties = $props();
14
+
15
+ let showImage = $derived(typeof image === 'string' && image.length > 0);
16
+ let showBadge = $derived(!hidden);
17
+ let isDot = $derived(mode === 'dot');
18
+
19
+ let standaloneRole = $derived(isDot ? 'presentation' : 'status');
20
+ let standaloneAriaLabel = $derived(
21
+ isDot ? null : (ariaLabel ?? (typeof value === 'string' ? value : null))
22
+ );
5
23
  </script>
6
24
 
7
- <div class="badge-icon {classes ?? ''}">
8
- <div class="badge-wrap">
9
- <img class="icon-img" src={image} alt="" />
10
- <div class="badge">{value}</div>
25
+ {#if showImage}
26
+ <div class="badge-icon {classes ?? ''}" data-pw={typeof testId === 'string' ? testId : null}>
27
+ <div class="badge-wrap">
28
+ <img class="icon-img" src={image} {alt} />
29
+ {#if showBadge}
30
+ <div class="badge" class:badge-dot={isDot}>{isDot ? '' : (value ?? '')}</div>
31
+ {/if}
32
+ </div>
11
33
  </div>
12
- </div>
34
+ {:else if showBadge}
35
+ <div
36
+ class="badge badge-standalone {classes ?? ''}"
37
+ class:badge-dot={isDot}
38
+ role={standaloneRole}
39
+ aria-label={standaloneAriaLabel}
40
+ data-pw={typeof testId === 'string' ? testId : null}
41
+ >
42
+ {isDot ? '' : (value ?? '')}
43
+ </div>
44
+ {/if}
13
45
 
14
46
  <style>
15
47
  .badge-wrap {
@@ -42,6 +74,18 @@
42
74
  z-index: 1;
43
75
  }
44
76
 
77
+ .badge-dot {
78
+ width: var(--badge-dot-size, 10px);
79
+ height: var(--badge-dot-size, 10px);
80
+ min-width: unset;
81
+ min-height: unset;
82
+ padding: 0;
83
+ }
84
+
85
+ .badge-standalone {
86
+ position: var(--badge-standalone-position, static);
87
+ }
88
+
45
89
  .icon-img {
46
90
  border-radius: var(--badge-img-border-radius, 6px);
47
91
  width: var(--badge-img-width, 64px);
@@ -1,5 +1,14 @@
1
- export type BadgeProperties = {
2
- image: string;
3
- value: string;
1
+ export type BadgeMode = 'count' | 'dot';
2
+ export type BadgeProperties = MandatoryBadgeProperties & OptionalBadgeProperties & BadgeEventProperties;
3
+ export type MandatoryBadgeProperties = Record<never, never>;
4
+ export type OptionalBadgeProperties = {
5
+ image?: string;
6
+ alt?: string;
7
+ value?: string;
8
+ mode?: BadgeMode;
9
+ hidden?: boolean;
10
+ ariaLabel?: string;
11
+ testId?: string;
4
12
  classes?: string;
5
13
  };
14
+ export type BadgeEventProperties = Record<never, never>;
@@ -3,7 +3,6 @@
3
3
  import Input from '../Input/Input.svelte';
4
4
  import type { InputButtonProperties } from './properties';
5
5
  import type { ValidationState } from '../types';
6
- import type { SvelteComponent } from 'svelte';
7
6
 
8
7
  let {
9
8
  value = $bindable(''),
@@ -17,12 +16,15 @@
17
16
  bottomButtonEventProperties,
18
17
  leftIcon,
19
18
  rightIcon,
20
- classes
19
+ classes,
20
+ mandatory,
21
+ size,
22
+ error
21
23
  }: InputButtonProperties = $props();
22
24
 
23
25
  let validationState = $state<ValidationState>('InProgress');
24
26
 
25
- let inputRef: SvelteComponent | null = $state(null);
27
+ let inputRef: ReturnType<typeof Input> | null = $state(null);
26
28
 
27
29
  // Derive enable state for right button
28
30
  const isRightButtonEnabled = $derived(validationState === 'Valid');
@@ -62,12 +64,14 @@
62
64
  <div class="container {classes ?? ''}">
63
65
  {#if inputProperties.label && inputProperties.label !== ''}
64
66
  <label class="label" for={inputProperties.name}>
65
- {inputProperties.label}
67
+ {inputProperties.label}{#if mandatory}<span class="mandatory-marker" aria-label="required">
68
+ *</span
69
+ >{/if}
66
70
  </label>
67
71
  {/if}
68
72
 
69
- <div class="input-button-container">
70
- <div class="input-button {validationState === 'Invalid' ? 'invalid' : 'valid'}">
73
+ <div class="input-button-container {size ? `size-${size}` : ''}">
74
+ <div class="input-button" class:invalid={validationState === 'Invalid'}>
71
75
  {#if leftButtonProperties != null}
72
76
  <div class="left-button">
73
77
  <Button {...leftButtonProperties} {...leftButtonEventProperties} icon={leftIcon} />
@@ -101,12 +105,14 @@
101
105
  </div>
102
106
  {/if}
103
107
  </div>
104
- {#if inputProperties.onErrorMessage !== '' && validationState === 'Invalid'}
108
+ {#if typeof error === 'string' && error.length > 0}
109
+ <div class="external-error-message">{error}</div>
110
+ {:else if typeof inputProperties.onErrorMessage === 'string' && inputProperties.onErrorMessage !== '' && validationState === 'Invalid'}
105
111
  <div class="error-message">
106
112
  {inputProperties.onErrorMessage}
107
113
  </div>
108
114
  {/if}
109
- {#if inputProperties.infoMessage !== ''}
115
+ {#if typeof inputProperties.infoMessage === 'string' && inputProperties.infoMessage !== ''}
110
116
  <div class="info-message">
111
117
  {inputProperties.infoMessage}
112
118
  </div>
@@ -127,9 +133,8 @@
127
133
  --input-box-shadow: none;
128
134
  --input-margin: none;
129
135
  --input-width: fit-content;
130
- height: var(--input-height, fit-content);
131
136
  font-size: var(--input-font-size, 16px) !important;
132
- font-weight: 500;
137
+ font-weight: var(--input-button-font-weight, 500);
133
138
  margin: var(--input-button-margin);
134
139
  border-radius: var(--input-button-radius, 4px);
135
140
  border: var(--input-button-container-border);
@@ -193,6 +198,30 @@
193
198
  margin: var(--input-btn-info-msg-margin, 12px 0px 0px 0px);
194
199
  }
195
200
 
201
+ .mandatory-marker {
202
+ color: var(--inputbutton-mandatory-color, #e11900);
203
+ }
204
+
205
+ .size-sm {
206
+ --input-height: var(--inputbutton-sm-height, 36px);
207
+ --input-padding: var(--inputbutton-sm-padding, 6px 12px);
208
+ }
209
+
210
+ .size-md {
211
+ --input-height: var(--inputbutton-md-height, 44px);
212
+ --input-padding: var(--inputbutton-md-padding, 10px 16px);
213
+ }
214
+
215
+ .size-lg {
216
+ --input-height: var(--inputbutton-lg-height, 54px);
217
+ --input-padding: var(--inputbutton-lg-padding, 14px 20px);
218
+ }
219
+
220
+ .external-error-message {
221
+ color: var(--inputbutton-external-error-color, #fa1405);
222
+ margin: var(--inputbutton-external-error-margin, 6px 0px 0px 0px);
223
+ }
224
+
196
225
  .left-button {
197
226
  --button-color: var(--left-button-color);
198
227
  --button-text-color: var(--left-button-text-color);
@@ -1,6 +1,16 @@
1
1
  import type { ButtonEventProperties, OptionalButtonProperties } from '../Button/properties';
2
2
  import type { InputEventProperties, OptionalInputProperties } from '../Input/properties';
3
3
  import type { Snippet } from 'svelte';
4
+ /**
5
+ * Preset size variants for the InputButton component.
6
+ * - `sm`: 36px height with compact padding (6px 12px)
7
+ * - `md`: 44px height with standard padding (10px 16px)
8
+ * - `lg`: 54px height with generous padding (14px 20px)
9
+ *
10
+ * All sizes are overridable via the corresponding CSS variables
11
+ * (`--inputbutton-sm-height`, `--inputbutton-md-padding`, etc.).
12
+ */
13
+ export type InputButtonSize = 'sm' | 'md' | 'lg';
4
14
  export type InputButtonProperties = OptionalInputButtonProperties & InputButtonEventProperties & {
5
15
  value: string;
6
16
  };
@@ -12,6 +22,17 @@ export type OptionalInputButtonProperties = {
12
22
  leftIcon?: Snippet;
13
23
  rightIcon?: Snippet;
14
24
  classes?: string;
25
+ /** When true, renders a red asterisk (*) next to the label to signal the field is required. */
26
+ mandatory?: boolean;
27
+ /** Preset height/padding size variant. Overridable per-size via CSS variables. */
28
+ size?: InputButtonSize;
29
+ /**
30
+ * External error message to display below the input-button row. Takes precedence over
31
+ * the internal `inputProperties.onErrorMessage` — when this prop is a non-empty string,
32
+ * the internal validation error is suppressed. Pass an empty string or omit to let
33
+ * internal validation messages surface normally.
34
+ */
35
+ error?: string;
15
36
  };
16
37
  export type InputButtonEventProperties = {
17
38
  inputEventProperties?: InputEventProperties;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.52.0",
3
+ "version": "2.54.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",