mertani-web-toolkit 0.1.28 → 0.1.30

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.
@@ -0,0 +1,4 @@
1
+ export interface IFieldOption {
2
+ id: string;
3
+ nama: string;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,151 @@
1
+ <!-- TODO: masih belum bisa dipake -->
2
+ <script lang="ts">
3
+ import { twMerge } from 'tailwind-merge';
4
+ import type { IFieldOption } from './Radio.js';
5
+
6
+ const {
7
+ id,
8
+ value = '',
9
+ options = [],
10
+ onChange,
11
+ required = false,
12
+ errorMessage,
13
+ disabled = false,
14
+ label = '',
15
+ subLabel = '',
16
+ class: className = '',
17
+ style: customStyle = ''
18
+ }: {
19
+ id: string;
20
+ value?: string;
21
+ options?: Array<IFieldOption>;
22
+ onChange?: (val: string) => void;
23
+ required?: boolean;
24
+ errorMessage?: string;
25
+ disabled?: boolean;
26
+ label?: string;
27
+ subLabel?: string;
28
+ class?: string;
29
+ style?: string;
30
+ } = $props();
31
+
32
+ const baseClasses = $derived(() => {
33
+ let classes = 'radio-container';
34
+ if (className) {
35
+ classes += ` ${className}`;
36
+ }
37
+ return classes;
38
+ });
39
+
40
+ const mergedClasses = $derived(twMerge(baseClasses(), className));
41
+ </script>
42
+
43
+ <div class={mergedClasses} style={customStyle}>
44
+ {#if label}
45
+ <label for={id} class="radio-label">
46
+ {label}
47
+ {#if subLabel}
48
+ <span class="label-subLabel">{subLabel}</span>
49
+ {/if}
50
+ </label>
51
+ {/if}
52
+
53
+ <div
54
+ class="flex flex-wrap items-center gap-6"
55
+ class:opacity-50={disabled}
56
+ class:pointer-events-none={disabled}
57
+ >
58
+ {#each options as option (option.id)}
59
+ <label
60
+ class="relative inline-flex items-center gap-2 text-sm text-[#212529] transition-colors"
61
+ class:active={value === option.id}
62
+ >
63
+ <input
64
+ type="radio"
65
+ class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
66
+ name={id}
67
+ style={customStyle}
68
+ value={option.id}
69
+ checked={value === option.id}
70
+ {disabled}
71
+ on:change={() => onChange?.(option.id)}
72
+ />
73
+ <span
74
+ class="flex h-4 w-4 items-center justify-center rounded-full border bg-white {value ===
75
+ option.id
76
+ ? 'border-4 border-[#ffa000]'
77
+ : 'border-[#ced4da]'}"
78
+ ></span>
79
+ <span>{option.nama}</span>
80
+ </label>
81
+ {/each}
82
+ </div>
83
+
84
+ {#if errorMessage && required}
85
+ <p class="mt-1 text-xs text-red-500">{errorMessage}</p>
86
+ {/if}
87
+ </div>
88
+
89
+ <style>
90
+ .radio-container {
91
+ display: flex;
92
+ flex-direction: column;
93
+ width: 100%;
94
+ }
95
+
96
+ .radio-label {
97
+ font-size: 14px;
98
+ font-weight: 500;
99
+ color: #212529;
100
+ margin-bottom: 8px;
101
+ display: block;
102
+ }
103
+
104
+ .label-subLabel {
105
+ font-weight: 400;
106
+ color: #98a2b3;
107
+ margin-left: 4px;
108
+ }
109
+
110
+ .segmented-group {
111
+ background: #fff;
112
+ }
113
+ .segmented-option {
114
+ border-right: 1px solid #ced4da;
115
+ &:last-child {
116
+ border-right: none;
117
+ }
118
+ &.active {
119
+ background: #ffa000;
120
+ color: #fff;
121
+ }
122
+ &:not(.active):hover {
123
+ background: #f2f4f7;
124
+ }
125
+ .text {
126
+ width: 100%;
127
+ text-align: center;
128
+ }
129
+ }
130
+
131
+ .title {
132
+ font-size: 1.125rem;
133
+ font-weight: bold;
134
+ margin-bottom: 1rem;
135
+ }
136
+
137
+ .title.horizontal {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ margin: 0;
142
+ }
143
+
144
+ .title.horizontal.left {
145
+ justify-content: flex-start;
146
+ }
147
+
148
+ .title.horizontal.right {
149
+ justify-content: flex-end;
150
+ }
151
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { IFieldOption } from './Radio.js';
2
+ type $$ComponentProps = {
3
+ id: string;
4
+ value?: string;
5
+ options?: Array<IFieldOption>;
6
+ onChange?: (val: string) => void;
7
+ required?: boolean;
8
+ errorMessage?: string;
9
+ disabled?: boolean;
10
+ label?: string;
11
+ subLabel?: string;
12
+ class?: string;
13
+ style?: string;
14
+ };
15
+ declare const Radio: import("svelte").Component<$$ComponentProps, {}, "">;
16
+ type Radio = ReturnType<typeof Radio>;
17
+ export default Radio;
@@ -0,0 +1,4 @@
1
+ export interface IFieldOption {
2
+ id: string;
3
+ nama: string;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,140 @@
1
+ <script lang="ts">
2
+ import { twMerge } from 'tailwind-merge';
3
+ import type { IFieldOption } from './Segmented.js';
4
+
5
+ const {
6
+ id,
7
+ value = '',
8
+ options = [],
9
+ onChange,
10
+ required = false,
11
+ errorMessage,
12
+ disabled = false,
13
+ label = '',
14
+ subLabel = '',
15
+ class: className = '',
16
+ style: customStyle = ''
17
+ }: {
18
+ id: string;
19
+ value?: string;
20
+ options?: Array<IFieldOption>;
21
+ onChange?: (val: string) => void;
22
+ required?: boolean;
23
+ errorMessage?: string;
24
+ disabled?: boolean;
25
+ label?: string;
26
+ subLabel?: string;
27
+ class?: string;
28
+ style?: string;
29
+ } = $props();
30
+
31
+ const baseClasses = $derived(() => {
32
+ let classes = 'segmented-group flex overflow-hidden rounded-lg border border-[#ced4da]';
33
+ if (className) {
34
+ classes += ` ${className}`;
35
+ }
36
+ return classes;
37
+ });
38
+
39
+ const mergedClasses = $derived(twMerge(baseClasses(), className));
40
+ </script>
41
+
42
+ <div class="radio-container" style={customStyle}>
43
+ {#if label}
44
+ <label for={id} class="radio-label">
45
+ {label}
46
+ {#if subLabel}
47
+ <span class="label-subLabel">{subLabel}</span>
48
+ {/if}
49
+ </label>
50
+ {/if}
51
+
52
+ <div class={mergedClasses} class:opacity-50={disabled} class:pointer-events-none={disabled}>
53
+ {#each options as option (option.id)}
54
+ <label
55
+ class="segmented-option relative inline-flex flex-1 cursor-pointer items-center justify-center px-4 py-2 text-sm font-medium text-[#475467] transition-colors"
56
+ class:active={value === option.id}
57
+ >
58
+ <input
59
+ type="radio"
60
+ class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
61
+ name={id}
62
+ style={customStyle}
63
+ value={option.id}
64
+ checked={value === option.id}
65
+ {disabled}
66
+ on:change={() => onChange?.(option.id)}
67
+ />
68
+ <span class="text">{option.nama}</span>
69
+ </label>
70
+ {/each}
71
+ </div>
72
+
73
+ {#if errorMessage && required}
74
+ <p class="mt-1 text-xs text-red-500">{errorMessage}</p>
75
+ {/if}
76
+ </div>
77
+
78
+ <style>
79
+ .radio-container {
80
+ display: flex;
81
+ flex-direction: column;
82
+ width: 100%;
83
+ }
84
+
85
+ .radio-label {
86
+ font-size: 14px;
87
+ font-weight: 500;
88
+ color: #212529;
89
+ margin-bottom: 8px;
90
+ display: block;
91
+ }
92
+
93
+ .label-subLabel {
94
+ font-weight: 400;
95
+ color: #98a2b3;
96
+ margin-left: 4px;
97
+ }
98
+
99
+ .segmented-group {
100
+ background: #fff;
101
+ }
102
+ .segmented-option {
103
+ border-right: 1px solid #ced4da;
104
+ &:last-child {
105
+ border-right: none;
106
+ }
107
+ &.active {
108
+ background: #ffa000;
109
+ color: #fff;
110
+ }
111
+ &:not(.active):hover {
112
+ background: #f2f4f7;
113
+ }
114
+ .text {
115
+ width: 100%;
116
+ text-align: center;
117
+ }
118
+ }
119
+
120
+ .title {
121
+ font-size: 1.125rem;
122
+ font-weight: bold;
123
+ margin-bottom: 1rem;
124
+ }
125
+
126
+ .title.horizontal {
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ margin: 0;
131
+ }
132
+
133
+ .title.horizontal.left {
134
+ justify-content: flex-start;
135
+ }
136
+
137
+ .title.horizontal.right {
138
+ justify-content: flex-end;
139
+ }
140
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { IFieldOption } from './Segmented.js';
2
+ type $$ComponentProps = {
3
+ id: string;
4
+ value?: string;
5
+ options?: Array<IFieldOption>;
6
+ onChange?: (val: string) => void;
7
+ required?: boolean;
8
+ errorMessage?: string;
9
+ disabled?: boolean;
10
+ label?: string;
11
+ subLabel?: string;
12
+ class?: string;
13
+ style?: string;
14
+ };
15
+ declare const Segmented: import("svelte").Component<$$ComponentProps, {}, "">;
16
+ type Segmented = ReturnType<typeof Segmented>;
17
+ export default Segmented;
@@ -12,7 +12,7 @@
12
12
  errorMessage,
13
13
  disabled = false,
14
14
  label = '',
15
- subTitle = '',
15
+ subLabel = '',
16
16
  placeholder = 'Pilih',
17
17
  searchPlaceholder = 'Cari',
18
18
  emptyMessage = 'Tidak ada hasil',
@@ -28,7 +28,7 @@
28
28
  errorMessage?: string;
29
29
  disabled?: boolean;
30
30
  label?: string;
31
- subTitle?: string;
31
+ subLabel?: string;
32
32
  placeholder?: string;
33
33
  searchPlaceholder?: string;
34
34
  emptyMessage?: string;
@@ -194,11 +194,8 @@
194
194
  {#if label}
195
195
  <label for={id} class="select-label">
196
196
  {label}
197
- {#if required}
198
- <span class="required-indicator">*</span>
199
- {/if}
200
- {#if subTitle}
201
- <span class="label-subtitle">{subTitle}</span>
197
+ {#if subLabel}
198
+ <span class="label-subLabel">{subLabel}</span>
202
199
  {/if}
203
200
  </label>
204
201
  {/if}
@@ -299,18 +296,12 @@
299
296
  display: block;
300
297
  }
301
298
 
302
- .label-subtitle {
299
+ .label-subLabel {
303
300
  font-weight: 400;
304
301
  color: #98a2b3;
305
302
  margin-left: 4px;
306
303
  }
307
304
 
308
- .required-indicator {
309
- color: #ef4444;
310
- margin-left: 4px;
311
- font-weight: 500;
312
- }
313
-
314
305
  .select-trigger {
315
306
  flex: 1;
316
307
  display: flex;
@@ -391,7 +382,7 @@
391
382
  }
392
383
 
393
384
  .select-option.selected {
394
- color: #f79b09;
385
+ color: #ffa000;
395
386
  }
396
387
 
397
388
  .select-empty {
@@ -8,7 +8,7 @@ type $$ComponentProps = {
8
8
  errorMessage?: string;
9
9
  disabled?: boolean;
10
10
  label?: string;
11
- subTitle?: string;
11
+ subLabel?: string;
12
12
  placeholder?: string;
13
13
  searchPlaceholder?: string;
14
14
  emptyMessage?: string;
@@ -0,0 +1,134 @@
1
+ <!-- TODO: masih belum bisa dipake -->
2
+ <script lang="ts">
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ const {
6
+ id,
7
+ value = '0',
8
+ min = 0,
9
+ max = Number.POSITIVE_INFINITY,
10
+ step = 1,
11
+ onChange,
12
+ label = '',
13
+ subLabel = '',
14
+ required = false,
15
+ errorMessage,
16
+ disabled = false,
17
+ class: className = '',
18
+ style: customStyle = ''
19
+ }: {
20
+ id: string;
21
+ value?: string | number;
22
+ min?: number;
23
+ max?: number;
24
+ step?: number;
25
+ onChange?: (val: string) => void;
26
+ label?: string;
27
+ subLabel?: string;
28
+ required?: boolean;
29
+ errorMessage?: string;
30
+ disabled?: boolean;
31
+ class?: string;
32
+ style?: string;
33
+ } = $props();
34
+
35
+ function toNum(v: any) {
36
+ const n = Number(v);
37
+ return isNaN(n) ? 0 : n;
38
+ }
39
+
40
+ function dec() {
41
+ const cur = toNum(value);
42
+ const next = Math.max(min, cur - (step ?? 1));
43
+ if (next !== cur) onChange?.(String(next));
44
+ }
45
+
46
+ function inc() {
47
+ const cur = toNum(value);
48
+ const next = Math.min(max, cur + (step ?? 1));
49
+ if (next !== cur) onChange?.(String(next));
50
+ }
51
+
52
+ const baseClasses = $derived(() => {
53
+ let classes = 'stepper-container';
54
+ if (className) {
55
+ classes += ` ${className}`;
56
+ }
57
+ return classes;
58
+ });
59
+
60
+ const mergedClasses = $derived(twMerge(baseClasses(), className));
61
+ </script>
62
+
63
+ <div class={mergedClasses} style={customStyle}>
64
+ {#if label}
65
+ <label for={id} class="stepper-label">
66
+ {label}
67
+ {#if subLabel}
68
+ <span class="label-subLabel">{subLabel}</span>
69
+ {/if}
70
+ </label>
71
+ {/if}
72
+ <div class="flex items-center">
73
+ <button
74
+ type="button"
75
+ class="grid h-9 w-9 place-items-center rounded-l-md border border-[#CED4DA] bg-[#E9ECEF] text-sm font-medium transition hover:bg-[#E9ECEF]"
76
+ on:click={dec}
77
+ style:color={toNum(value) <= (min ?? Number.NEGATIVE_INFINITY) || disabled
78
+ ? '#CED4DA'
79
+ : '#6C757D'}
80
+ {disabled}
81
+ >
82
+ -
83
+ </button>
84
+ <input
85
+ {id}
86
+ type="number"
87
+ class="h-9 w-16 border-t border-r-0 border-b border-l-0 border-[#CED4DA] bg-white pl-3 text-center text-sm font-medium text-[#2C2C30]"
88
+ value={value as any}
89
+ placeholder={''}
90
+ {disabled}
91
+ readonly
92
+ />
93
+ <button
94
+ type="button"
95
+ class="grid h-9 w-9 place-items-center rounded-r-md border border-[#CED4DA] bg-[#E9ECEF] text-sm font-medium text-[#6C757D] transition hover:bg-[#E9ECEF]"
96
+ on:click={inc}
97
+ style:color={toNum(value) >= (max ?? Number.POSITIVE_INFINITY) || disabled
98
+ ? '#CED4DA'
99
+ : '#6C757D'}
100
+ {disabled}
101
+ >
102
+ +
103
+ </button>
104
+ </div>
105
+
106
+ {#if errorMessage && required}
107
+ <p class="error-message">{errorMessage}</p>
108
+ {/if}
109
+ </div>
110
+
111
+ <style>
112
+ .stepper-container {
113
+ display: flex;
114
+ flex-direction: column;
115
+ width: 100%;
116
+ }
117
+ .stepper-label {
118
+ font-size: 14px;
119
+ font-weight: 500;
120
+ color: #212529;
121
+ margin-bottom: 8px;
122
+ display: block;
123
+ }
124
+ .label-subLabel {
125
+ font-weight: 400;
126
+ color: #98a2b3;
127
+ margin-left: 4px;
128
+ }
129
+ .error-message {
130
+ font-size: 12px;
131
+ color: #ef4444;
132
+ margin-top: 4px;
133
+ }
134
+ </style>
@@ -0,0 +1,18 @@
1
+ type $$ComponentProps = {
2
+ id: string;
3
+ value?: string | number;
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ onChange?: (val: string) => void;
8
+ label?: string;
9
+ subLabel?: string;
10
+ required?: boolean;
11
+ errorMessage?: string;
12
+ disabled?: boolean;
13
+ class?: string;
14
+ style?: string;
15
+ };
16
+ declare const Steppers: import("svelte").Component<$$ComponentProps, {}, "">;
17
+ type Steppers = ReturnType<typeof Steppers>;
18
+ export default Steppers;
@@ -10,7 +10,7 @@
10
10
  value = '',
11
11
  placeholder = '',
12
12
  label = '',
13
- subTitle = '',
13
+ subLabel = '',
14
14
  prefix = '',
15
15
  suffix = '',
16
16
  rightIcon,
@@ -29,7 +29,7 @@
29
29
  value?: string | number;
30
30
  placeholder?: string;
31
31
  label?: string;
32
- subTitle?: string;
32
+ subLabel?: string;
33
33
  prefix?: string;
34
34
  suffix?: string;
35
35
  rightIcon?: TIconName;
@@ -114,8 +114,8 @@
114
114
  {#if label}
115
115
  <label for={id} class="input-label">
116
116
  {label}
117
- {#if subTitle}
118
- <span class="label-subtitle">{subTitle}</span>
117
+ {#if subLabel}
118
+ <span class="label-subLabel">{subLabel}</span>
119
119
  {/if}
120
120
  </label>
121
121
  {/if}
@@ -173,7 +173,7 @@
173
173
  display: block;
174
174
  }
175
175
 
176
- .label-subtitle {
176
+ .label-subLabel {
177
177
  font-weight: 400;
178
178
  color: #98a2b3;
179
179
  margin-left: 4px;
@@ -5,7 +5,7 @@ type $$ComponentProps = {
5
5
  value?: string | number;
6
6
  placeholder?: string;
7
7
  label?: string;
8
- subTitle?: string;
8
+ subLabel?: string;
9
9
  prefix?: string;
10
10
  suffix?: string;
11
11
  rightIcon?: TIconName;
@@ -7,7 +7,7 @@
7
7
  value = '',
8
8
  placeholder = '',
9
9
  label = '',
10
- subTitle = '',
10
+ subLabel = '',
11
11
  rows = 4,
12
12
  disabled = false,
13
13
  readOnly = false,
@@ -21,7 +21,7 @@
21
21
  value?: string;
22
22
  placeholder?: string;
23
23
  label?: string;
24
- subTitle?: string;
24
+ subLabel?: string;
25
25
  rows?: number;
26
26
  disabled?: boolean;
27
27
  readOnly?: boolean;
@@ -52,8 +52,8 @@
52
52
  {#if label}
53
53
  <label for={id} class="input-label">
54
54
  {label}
55
- {#if subTitle}
56
- <span class="label-subtitle">{subTitle}</span>
55
+ {#if subLabel}
56
+ <span class="label-subLabel">{subLabel}</span>
57
57
  {/if}
58
58
  </label>
59
59
  {/if}
@@ -90,7 +90,7 @@
90
90
  display: block;
91
91
  }
92
92
 
93
- .label-subtitle {
93
+ .label-subLabel {
94
94
  font-weight: 400;
95
95
  color: #98a2b3;
96
96
  margin-left: 4px;
@@ -3,7 +3,7 @@ type $$ComponentProps = {
3
3
  value?: string;
4
4
  placeholder?: string;
5
5
  label?: string;
6
- subTitle?: string;
6
+ subLabel?: string;
7
7
  rows?: number;
8
8
  disabled?: boolean;
9
9
  readOnly?: boolean;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,9 @@ export { default as Icon } from './components/Icon/Icon.svelte';
3
3
  export { default as TextInput } from './components/inputs/TextInput/TextInput.svelte';
4
4
  export { default as TextareaInput } from './components/inputs/TextareaInput/TextareaInput.svelte';
5
5
  export { default as SelectInput } from './components/inputs/SelectInput/SelectInput.svelte';
6
+ export { default as Radio } from './components/inputs/Radio/Radio.svelte';
7
+ export { default as Segmented } from './components/inputs/Segmented/Segmented.svelte';
8
+ export { default as Steppers } from './components/inputs/Steppers/Steppers.svelte';
6
9
  export { default as IMSLayout1 } from './layouts/IMS/IMSLayout1/IMSLayout1.svelte';
7
10
  export { default as Table } from './components/Table/Table.svelte';
8
11
  export { default as HeaderContent } from './components/HeaderContent/HeaderContent.svelte';
package/dist/index.js CHANGED
@@ -4,6 +4,9 @@ export { default as Icon } from './components/Icon/Icon.svelte';
4
4
  export { default as TextInput } from './components/inputs/TextInput/TextInput.svelte';
5
5
  export { default as TextareaInput } from './components/inputs/TextareaInput/TextareaInput.svelte';
6
6
  export { default as SelectInput } from './components/inputs/SelectInput/SelectInput.svelte';
7
+ export { default as Radio } from './components/inputs/Radio/Radio.svelte';
8
+ export { default as Segmented } from './components/inputs/Segmented/Segmented.svelte';
9
+ export { default as Steppers } from './components/inputs/Steppers/Steppers.svelte';
7
10
  export { default as IMSLayout1 } from './layouts/IMS/IMSLayout1/IMSLayout1.svelte';
8
11
  export { default as Table } from './components/Table/Table.svelte';
9
12
  export { default as HeaderContent } from './components/HeaderContent/HeaderContent.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mertani-web-toolkit",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "homepage": "https://storybook.mertani.com/",
5
5
  "scripts": {
6
6
  "dev": "vite dev",