@keenthemes/ktui 1.0.20 → 1.0.21

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.
Files changed (98) hide show
  1. package/dist/ktui.js +418 -144
  2. package/dist/ktui.min.js +1 -1
  3. package/dist/ktui.min.js.map +1 -1
  4. package/dist/styles.css +139 -31
  5. package/examples/image-input/file-upload-example.html +189 -0
  6. package/examples/select/remote-data_.html +5 -0
  7. package/examples/select/test-optimizations.html +227 -0
  8. package/examples/select/test-remote-search.html +151 -0
  9. package/examples/sticky/README.md +158 -0
  10. package/examples/sticky/debug-sticky.html +144 -0
  11. package/examples/sticky/test-runner.html +175 -0
  12. package/examples/sticky/test-sticky-logic.js +369 -0
  13. package/examples/sticky/test-sticky-positioning.html +386 -0
  14. package/examples/toast/example.html +52 -0
  15. package/lib/cjs/components/component.js +5 -3
  16. package/lib/cjs/components/component.js.map +1 -1
  17. package/lib/cjs/components/datatable/datatable-sort.js +4 -0
  18. package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
  19. package/lib/cjs/components/datatable/datatable.js +9 -3
  20. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  21. package/lib/cjs/components/image-input/image-input.js +10 -2
  22. package/lib/cjs/components/image-input/image-input.js.map +1 -1
  23. package/lib/cjs/components/select/combobox.js +50 -20
  24. package/lib/cjs/components/select/combobox.js.map +1 -1
  25. package/lib/cjs/components/select/dropdown.js +4 -2
  26. package/lib/cjs/components/select/dropdown.js.map +1 -1
  27. package/lib/cjs/components/select/index.js.map +1 -1
  28. package/lib/cjs/components/select/option.js +2 -1
  29. package/lib/cjs/components/select/option.js.map +1 -1
  30. package/lib/cjs/components/select/remote.js +50 -50
  31. package/lib/cjs/components/select/remote.js.map +1 -1
  32. package/lib/cjs/components/select/search.js +7 -5
  33. package/lib/cjs/components/select/search.js.map +1 -1
  34. package/lib/cjs/components/select/select.js +199 -33
  35. package/lib/cjs/components/select/select.js.map +1 -1
  36. package/lib/cjs/components/select/tags.js +3 -1
  37. package/lib/cjs/components/select/tags.js.map +1 -1
  38. package/lib/cjs/components/select/templates.js.map +1 -1
  39. package/lib/cjs/components/select/utils.js +23 -10
  40. package/lib/cjs/components/select/utils.js.map +1 -1
  41. package/lib/cjs/components/sticky/sticky.js +52 -14
  42. package/lib/cjs/components/sticky/sticky.js.map +1 -1
  43. package/lib/esm/components/component.js +5 -3
  44. package/lib/esm/components/component.js.map +1 -1
  45. package/lib/esm/components/datatable/datatable-sort.js +4 -0
  46. package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
  47. package/lib/esm/components/datatable/datatable.js +9 -3
  48. package/lib/esm/components/datatable/datatable.js.map +1 -1
  49. package/lib/esm/components/image-input/image-input.js +10 -2
  50. package/lib/esm/components/image-input/image-input.js.map +1 -1
  51. package/lib/esm/components/select/combobox.js +50 -20
  52. package/lib/esm/components/select/combobox.js.map +1 -1
  53. package/lib/esm/components/select/dropdown.js +4 -2
  54. package/lib/esm/components/select/dropdown.js.map +1 -1
  55. package/lib/esm/components/select/index.js +1 -1
  56. package/lib/esm/components/select/index.js.map +1 -1
  57. package/lib/esm/components/select/option.js +2 -1
  58. package/lib/esm/components/select/option.js.map +1 -1
  59. package/lib/esm/components/select/remote.js +50 -50
  60. package/lib/esm/components/select/remote.js.map +1 -1
  61. package/lib/esm/components/select/search.js +8 -6
  62. package/lib/esm/components/select/search.js.map +1 -1
  63. package/lib/esm/components/select/select.js +199 -33
  64. package/lib/esm/components/select/select.js.map +1 -1
  65. package/lib/esm/components/select/tags.js +3 -1
  66. package/lib/esm/components/select/tags.js.map +1 -1
  67. package/lib/esm/components/select/templates.js.map +1 -1
  68. package/lib/esm/components/select/utils.js +23 -10
  69. package/lib/esm/components/select/utils.js.map +1 -1
  70. package/lib/esm/components/sticky/sticky.js +52 -14
  71. package/lib/esm/components/sticky/sticky.js.map +1 -1
  72. package/package.json +1 -1
  73. package/src/components/component.ts +12 -11
  74. package/src/components/datatable/datatable-sort.ts +6 -0
  75. package/src/components/datatable/datatable.ts +90 -81
  76. package/src/components/image-input/image-input.ts +11 -2
  77. package/src/components/image-input/types.ts +1 -0
  78. package/src/components/input/input-group.css +1 -1
  79. package/src/components/input/input.css +1 -1
  80. package/src/components/scrollable/scrollable.css +3 -3
  81. package/src/components/select/combobox.ts +84 -34
  82. package/src/components/select/dropdown.ts +20 -11
  83. package/src/components/select/index.ts +6 -1
  84. package/src/components/select/option.ts +7 -6
  85. package/src/components/select/remote.ts +51 -52
  86. package/src/components/select/search.ts +51 -45
  87. package/src/components/select/select.css +12 -11
  88. package/src/components/select/select.ts +371 -102
  89. package/src/components/select/tags.ts +9 -3
  90. package/src/components/select/templates.ts +1 -4
  91. package/src/components/select/utils.ts +55 -20
  92. package/src/components/select/variants.css +0 -1
  93. package/src/components/sticky/sticky.ts +47 -16
  94. package/src/components/sticky/types.ts +3 -0
  95. package/src/components/table/table.css +1 -1
  96. package/src/components/textarea/textarea.css +1 -1
  97. package/src/components/toast/toast.css +84 -47
  98. package/src/components/toast/types.ts +3 -0
@@ -39,7 +39,9 @@ export class KTSelectTags {
39
39
  if (!wrapper) return;
40
40
 
41
41
  // Remove all previous tags
42
- Array.from(wrapper.querySelectorAll('[data-kt-select-tag]')).forEach(tag => tag.remove());
42
+ Array.from(wrapper.querySelectorAll('[data-kt-select-tag]')).forEach(
43
+ (tag) => tag.remove(),
44
+ );
43
45
 
44
46
  // If no options selected, do nothing (let display show placeholder)
45
47
  if (selectedOptions.length === 0) {
@@ -58,7 +60,9 @@ export class KTSelectTags {
58
60
  }
59
61
  }
60
62
  if (!optionElement) {
61
- const originalOptions = this._select.getElement().querySelectorAll('option');
63
+ const originalOptions = this._select
64
+ .getElement()
65
+ .querySelectorAll('option');
62
66
  for (const opt of Array.from(originalOptions)) {
63
67
  if ((opt as HTMLOptionElement).value === optionValue) {
64
68
  optionElement = opt as HTMLOptionElement;
@@ -70,7 +74,9 @@ export class KTSelectTags {
70
74
  const tag = defaultTemplates.tag(optionElement, this._config);
71
75
 
72
76
  // Add event listener to the close button
73
- const closeButton = tag.querySelector('[data-kt-select-remove-button]') as HTMLElement;
77
+ const closeButton = tag.querySelector(
78
+ '[data-kt-select-remove-button]',
79
+ ) as HTMLElement;
74
80
  if (closeButton) {
75
81
  this._eventManager.addListener(closeButton, 'click', (event: Event) => {
76
82
  event.stopPropagation();
@@ -468,10 +468,7 @@ export const defaultTemplates: KTSelectTemplateInterface = {
468
468
  selectAll: (config: KTSelectConfigInterface): HTMLElement => {
469
469
  const template = getTemplateStrings(config).selectAll;
470
470
  const element = stringToElement(
471
- template.replace(
472
- '{{text}}',
473
- config.selectAllText || 'Select All',
474
- ),
471
+ template.replace('{{text}}', config.selectAllText || 'Select All'),
475
472
  );
476
473
  return element;
477
474
  },
@@ -53,14 +53,17 @@ export function filterOptions(
53
53
 
54
54
  for (const option of options) {
55
55
  // Use data-text for matching if available, otherwise fall back to textContent
56
- const optionText = (option.dataset.text || option.textContent || '').toLowerCase();
56
+ const optionText = (
57
+ option.dataset.text ||
58
+ option.textContent ||
59
+ ''
60
+ ).toLowerCase();
57
61
  const isMatch = optionText.includes(queryLower);
58
62
 
59
63
  if (isMatch) {
60
64
  option.classList.remove('hidden');
61
65
  if (option.style.display === 'none') option.style.display = ''; // Ensure visible
62
66
  visibleOptionsCount++;
63
-
64
67
  } else {
65
68
  option.classList.add('hidden');
66
69
  }
@@ -89,7 +92,9 @@ export class FocusManager {
89
92
  private _focusClass: string;
90
93
  private _hoverClass: string;
91
94
  private _eventManager: EventManager;
92
- private _onFocusChange: ((option: HTMLElement | null, index: number | null) => void) | null = null;
95
+ private _onFocusChange:
96
+ | ((option: HTMLElement | null, index: number | null) => void)
97
+ | null = null;
93
98
 
94
99
  constructor(
95
100
  element: HTMLElement,
@@ -149,7 +154,10 @@ export class FocusManager {
149
154
  if (options.length === 0) return null;
150
155
  for (let i = 0; i < options.length; i++) {
151
156
  const option = options[i];
152
- if (!option.classList.contains('disabled') && option.getAttribute('aria-disabled') !== 'true') {
157
+ if (
158
+ !option.classList.contains('disabled') &&
159
+ option.getAttribute('aria-disabled') !== 'true'
160
+ ) {
153
161
  this.resetFocus();
154
162
  this._focusedOptionIndex = i;
155
163
  this.applyFocus(option);
@@ -168,7 +176,10 @@ export class FocusManager {
168
176
  if (options.length === 0) return null;
169
177
  for (let i = options.length - 1; i >= 0; i--) {
170
178
  const option = options[i];
171
- if (!option.classList.contains('disabled') && option.getAttribute('aria-disabled') !== 'true') {
179
+ if (
180
+ !option.classList.contains('disabled') &&
181
+ option.getAttribute('aria-disabled') !== 'true'
182
+ ) {
172
183
  this.resetFocus();
173
184
  this._focusedOptionIndex = i;
174
185
  this.applyFocus(option);
@@ -193,7 +204,8 @@ export class FocusManager {
193
204
  if (
194
205
  !option.classList.contains('disabled') &&
195
206
  option.getAttribute('aria-disabled') !== 'true' &&
196
- (option.textContent?.toLowerCase().startsWith(lowerStr) || option.dataset.value?.toLowerCase().startsWith(lowerStr))
207
+ (option.textContent?.toLowerCase().startsWith(lowerStr) ||
208
+ option.dataset.value?.toLowerCase().startsWith(lowerStr))
197
209
  ) {
198
210
  this.resetFocus();
199
211
  this._focusedOptionIndex = idx;
@@ -211,11 +223,17 @@ export class FocusManager {
211
223
  public focusNext(): HTMLElement | null {
212
224
  const options = this.getVisibleOptions();
213
225
  if (options.length === 0) return null;
214
- let idx = this._focusedOptionIndex === null ? 0 : (this._focusedOptionIndex + 1) % options.length;
226
+ let idx =
227
+ this._focusedOptionIndex === null
228
+ ? 0
229
+ : (this._focusedOptionIndex + 1) % options.length;
215
230
  let startIdx = idx;
216
231
  do {
217
232
  const option = options[idx];
218
- if (!option.classList.contains('disabled') && option.getAttribute('aria-disabled') !== 'true') {
233
+ if (
234
+ !option.classList.contains('disabled') &&
235
+ option.getAttribute('aria-disabled') !== 'true'
236
+ ) {
219
237
  this.resetFocus();
220
238
  this._focusedOptionIndex = idx;
221
239
  this.applyFocus(option);
@@ -233,11 +251,17 @@ export class FocusManager {
233
251
  public focusPrevious(): HTMLElement | null {
234
252
  const options = this.getVisibleOptions();
235
253
  if (options.length === 0) return null;
236
- let idx = this._focusedOptionIndex === null ? options.length - 1 : (this._focusedOptionIndex - 1 + options.length) % options.length;
254
+ let idx =
255
+ this._focusedOptionIndex === null
256
+ ? options.length - 1
257
+ : (this._focusedOptionIndex - 1 + options.length) % options.length;
237
258
  let startIdx = idx;
238
259
  do {
239
260
  const option = options[idx];
240
- if (!option.classList.contains('disabled') && option.getAttribute('aria-disabled') !== 'true') {
261
+ if (
262
+ !option.classList.contains('disabled') &&
263
+ option.getAttribute('aria-disabled') !== 'true'
264
+ ) {
241
265
  this.resetFocus();
242
266
  this._focusedOptionIndex = idx;
243
267
  this.applyFocus(option);
@@ -255,7 +279,10 @@ export class FocusManager {
255
279
  public applyFocus(option: HTMLElement): void {
256
280
  if (!option) return;
257
281
  // Ensure it's not disabled
258
- if (option.classList.contains('disabled') || option.getAttribute('aria-disabled') === 'true') {
282
+ if (
283
+ option.classList.contains('disabled') ||
284
+ option.getAttribute('aria-disabled') === 'true'
285
+ ) {
259
286
  return;
260
287
  }
261
288
  // DO NOT CALL resetFocus() here. Caller's responsibility.
@@ -269,7 +296,9 @@ export class FocusManager {
269
296
  * Reset focus on all options
270
297
  */
271
298
  public resetFocus(): void {
272
- const focusedElements = this._element.querySelectorAll(`.${this._focusClass}, .${this._hoverClass}`);
299
+ const focusedElements = this._element.querySelectorAll(
300
+ `.${this._focusClass}, .${this._hoverClass}`,
301
+ );
273
302
 
274
303
  // Remove focus and hover classes from all options
275
304
  focusedElements.forEach((element) => {
@@ -285,9 +314,7 @@ export class FocusManager {
285
314
  public scrollIntoView(option: HTMLElement): void {
286
315
  if (!option) return;
287
316
 
288
- const container = this._element.querySelector(
289
- '[data-kt-select-options]',
290
- );
317
+ const container = this._element.querySelector('[data-kt-select-options]');
291
318
  if (!container) return;
292
319
 
293
320
  const optionRect = option.getBoundingClientRect();
@@ -312,7 +339,10 @@ export class FocusManager {
312
339
 
313
340
  if (index >= 0) {
314
341
  const optionToFocus = options[index];
315
- if (!optionToFocus.classList.contains('disabled') && optionToFocus.getAttribute('aria-disabled') !== 'true'){
342
+ if (
343
+ !optionToFocus.classList.contains('disabled') &&
344
+ optionToFocus.getAttribute('aria-disabled') !== 'true'
345
+ ) {
316
346
  this.resetFocus();
317
347
  this._focusedOptionIndex = index;
318
348
  this.applyFocus(optionToFocus);
@@ -357,7 +387,9 @@ export class FocusManager {
357
387
  /**
358
388
  * Set a callback to be called when focus changes
359
389
  */
360
- public setOnFocusChange(cb: (option: HTMLElement | null, index: number | null) => void) {
390
+ public setOnFocusChange(
391
+ cb: (option: HTMLElement | null, index: number | null) => void,
392
+ ) {
361
393
  this._onFocusChange = cb;
362
394
  }
363
395
 
@@ -480,9 +512,12 @@ export function debounce(
480
512
  * Replaces all {{key}} in the template with the corresponding value from the data object.
481
513
  * If a key is missing in data, replaces with an empty string.
482
514
  */
483
- export function renderTemplateString(template: string, data: Record<string, any>): string {
515
+ export function renderTemplateString(
516
+ template: string,
517
+ data: Record<string, any>,
518
+ ): string {
484
519
  return template.replace(/{{(\w+)}}/g, (_, key) =>
485
- data[key] !== undefined && data[key] !== null ? String(data[key]) : ''
520
+ data[key] !== undefined && data[key] !== null ? String(data[key]) : '',
486
521
  );
487
522
  }
488
523
 
@@ -518,4 +553,4 @@ export function stringToElement(html: string): HTMLElement {
518
553
  const template = document.createElement('template');
519
554
  template.innerHTML = html.trim();
520
555
  return template.content.firstElementChild as HTMLElement;
521
- }
556
+ }
@@ -2,4 +2,3 @@
2
2
  * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
3
3
  * Copyright 2025 by Keenthemes Inc
4
4
  */
5
-
@@ -23,7 +23,10 @@ export class KTSticky extends KTComponent implements KTStickyInterface {
23
23
  name: '',
24
24
  class: '',
25
25
  top: '',
26
+ middle: false,
27
+ bottom: '',
26
28
  start: '',
29
+ center: false,
27
30
  end: '',
28
31
  width: '',
29
32
  zindex: '',
@@ -212,7 +215,10 @@ export class KTSticky extends KTComponent implements KTStickyInterface {
212
215
 
213
216
  let width = this._getOption('width') as string;
214
217
  const top = this._getOption('top') as string;
218
+ const middle = this._getOption('middle') as boolean;
219
+ const bottom = this._getOption('bottom') as string;
215
220
  const start = this._getOption('start') as string;
221
+ const center = this._getOption('center') as boolean;
216
222
  const end = this._getOption('end') as string;
217
223
  const height = this._calculateHeight();
218
224
  const zindex = this._getOption('zindex') as string;
@@ -233,29 +239,49 @@ export class KTSticky extends KTComponent implements KTStickyInterface {
233
239
  this._element.style.width = `${Math.round(parseFloat(width))}px`;
234
240
  }
235
241
 
236
- if (top) {
237
- this._element.style.top = `${top}px`;
238
- }
239
-
240
- if (start) {
241
- if (start === 'auto') {
242
- const offsetLeft = KTDom.offset(this._element).left;
243
- if (offsetLeft >= 0) {
244
- this._element.style.insetInlineStart = `${offsetLeft}px`;
242
+ if (middle === true) {
243
+ this._element.style.insetBlockStart = `50%`;
244
+ } else {
245
+ if (top) {
246
+ if (top === 'auto') {
247
+ this._element.style.insetBlockStart = `0px`;
248
+ } else {
249
+ this._element.style.insetBlockStart = `${top}px`;
245
250
  }
246
251
  } else {
247
- this._element.style.insetInlineStart = `${start}px`;
252
+ if (bottom) {
253
+ if (bottom === 'auto') {
254
+ this._element.style.insetBlockEnd = `0px`;
255
+ } else {
256
+ this._element.style.insetBlockEnd = `${bottom}px`;
257
+ }
258
+ }
248
259
  }
249
260
  }
250
261
 
251
- if (end) {
252
- if (end === 'auto') {
253
- const offseRight = KTDom.offset(this._element).right;
254
- if (offseRight >= 0) {
255
- this._element.style.insetInlineEnd = `${offseRight}px`;
262
+ if (center === true) {
263
+ this._element.style.insetInlineStart = `50%`;
264
+ } else {
265
+ if (start) {
266
+ if (start === 'auto') {
267
+ const offsetLeft = KTDom.offset(this._element).left;
268
+ if (offsetLeft >= 0) {
269
+ this._element.style.insetInlineStart = `${offsetLeft}px`;
270
+ }
271
+ } else {
272
+ this._element.style.insetInlineStart = `${start}px`;
256
273
  }
257
274
  } else {
258
- this._element.style.insetInlineEnd = `${end}px`;
275
+ if (end) {
276
+ if (end === 'auto') {
277
+ const offsetRight = KTDom.offset(this._element).right;
278
+ if (offsetRight >= 0) {
279
+ this._element.style.insetInlineEnd = `${offsetRight}px`;
280
+ }
281
+ } else {
282
+ this._element.style.insetInlineEnd = `${end}px`;
283
+ }
284
+ }
259
285
  }
260
286
  }
261
287
 
@@ -282,6 +308,11 @@ export class KTSticky extends KTComponent implements KTStickyInterface {
282
308
  if (!this._element) return;
283
309
 
284
310
  this._element.style.top = '';
311
+ this._element.style.bottom = '';
312
+ this._element.style.insetInlineStart = '';
313
+ this._element.style.insetInlineEnd = '';
314
+ this._element.style.insetBlockStart = '';
315
+ this._element.style.insetBlockEnd = '';
285
316
  this._element.style.width = '';
286
317
  this._element.style.left = '';
287
318
  this._element.style.right = '';
@@ -9,7 +9,10 @@ export interface KTStickyConfigInterface {
9
9
  class: string;
10
10
  zindex: string;
11
11
  top: string;
12
+ middle: boolean;
13
+ bottom: string;
12
14
  start: string;
15
+ center: boolean;
13
16
  end: string;
14
17
  width: string | number | object;
15
18
  offset: number;
@@ -46,7 +46,7 @@
46
46
 
47
47
  td,
48
48
  th {
49
- input[type=checkbox] {
49
+ input[type='checkbox'] {
50
50
  vertical-align: inherit;
51
51
  }
52
52
  }
@@ -9,7 +9,7 @@
9
9
  .kt-textarea {
10
10
  @apply w-full bg-background border border-input text-foreground shadow-xs shadow-[rgba(0,0,0,0.05)] transition-[color,box-shadow] placeholder:text-muted-foreground/80;
11
11
  @apply focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/30;
12
- @apply disabled:cursor-not-allowed disabled:opacity-60;
12
+ @apply disabled:cursor-not-allowed disabled:opacity-60;
13
13
  @apply [&[readonly]]:bg-muted/80 [&[readonly]]:cursor-not-allowed;
14
14
  @apply aria-invalid:border-destructive/60 aria-invalid:ring-destructive/10;
15
15
  }
@@ -3,58 +3,95 @@
3
3
  * Copyright 2025 by Keenthemes Inc
4
4
  */
5
5
 
6
- /* Base Styles */
7
- @layer components {
8
- /* Container */
9
- .kt-toast-container {
10
- @apply fixed z-9999;
11
- }
12
-
13
- /* Toast */
14
- .kt-toast {
15
- @apply fixed z-9999 max-w-[95%] w-76 shadow-sm pointer-events-auto overflow-hidden;
16
-
17
- opacity: 0;
18
- animation: kt-toast-in 0.28s cubic-bezier(.4,0,.2,1) forwards;
19
- transition: top 0.28s cubic-bezier(.4,0,.2,1), opacity 0.28s cubic-bezier(.4,0,.2,1);
20
-
21
- &.kt-toast-top-end { @apply bottom-auto top-0 end-0; }
22
- &.kt-toast-top-center { @apply bottom-auto top-0 start-1/2 -translate-x-1/2; }
23
- &.kt-toast-top-start { @apply bottom-auto top-0 start-0; }
24
- &.kt-toast-bottom-end { @apply top-auto bottom-0 end-0; }
25
- &.kt-toast-bottom-center { @apply top-auto bottom-0 start-1/2 -translate-x-1/2; }
26
- &.kt-toast-bottom-start { @apply top-auto bottom-0 start-0; }
27
- }
28
-
29
- /* Progress */
30
- .kt-toast-progress {
31
- @apply fixed start-0 bottom-0 w-full h-[3px] bg-primary;
32
- transform-origin: left;
33
- animation: kt-toast-progress-line linear forwards;
34
- }
35
- }
6
+ /* Base Styles */
7
+ @layer components {
8
+ /* Container */
9
+ .kt-toast-container {
10
+ @apply fixed z-9999;
11
+ }
12
+
13
+ /* Toast */
14
+ .kt-toast {
15
+ @apply fixed z-9999 max-w-[95%] w-76 shadow-sm pointer-events-auto overflow-hidden;
16
+
17
+ opacity: 0;
18
+ animation: kt-toast-in 0.28s cubic-bezier(0.4, 0, 0.2, 1) forwards;
19
+ transition:
20
+ top 0.28s cubic-bezier(0.4, 0, 0.2, 1),
21
+ opacity 0.28s cubic-bezier(0.4, 0, 0.2, 1);
22
+
23
+ &.kt-toast-top-end {
24
+ @apply bottom-auto top-0 end-0;
25
+ }
26
+ &.kt-toast-top-center {
27
+ @apply bottom-auto top-0 left-1/2 -translate-x-1/2;
28
+ }
29
+ &.kt-toast-top-start {
30
+ @apply bottom-auto top-0 start-0;
31
+ }
32
+ &.kt-toast-middle-end {
33
+ @apply top-1/2 -translate-y-1/2 end-0;
34
+ }
35
+ &.kt-toast-middle-center {
36
+ @apply top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2;
37
+ }
38
+ &.kt-toast-middle-start {
39
+ @apply top-1/2 -translate-y-1/2 start-0;
40
+ }
41
+ &.kt-toast-bottom-end {
42
+ @apply top-auto bottom-0 end-0;
43
+ }
44
+ &.kt-toast-bottom-center {
45
+ @apply top-auto bottom-0 left-1/2 -translate-x-1/2;
46
+ }
47
+ &.kt-toast-bottom-start {
48
+ @apply top-auto bottom-0 start-0;
49
+ }
50
+ }
51
+
52
+ /* Progress */
53
+ .kt-toast-progress {
54
+ @apply fixed start-0 bottom-0 w-full h-[3px] bg-primary;
55
+ transform-origin: left;
56
+ animation: kt-toast-progress-line linear forwards;
57
+ }
58
+ }
36
59
 
37
60
  /* RTL Styles */
38
61
  @layer components {
39
62
  [dir='rtl'] .kt-toast-progress {
40
- transform-origin: right;
41
- }
63
+ transform-origin: right;
64
+ }
42
65
  }
43
66
 
44
67
  /* Animations */
45
68
  @layer components {
46
- @keyframes kt-toast-in {
47
- from { opacity: 0; transform: translateY(-24px); }
48
- to { opacity: 1; transform: translateY(0); }
49
- }
50
-
51
- @keyframes kt-toast-out {
52
- from { opacity: 1; }
53
- to { opacity: 0; }
54
- }
55
-
56
- @keyframes kt-toast-progress-line {
57
- from { transform: scaleX(1); }
58
- to { transform: scaleX(0); }
59
- }
60
- }
69
+ @keyframes kt-toast-in {
70
+ from {
71
+ opacity: 0;
72
+ transform: translateY(-24px);
73
+ }
74
+ to {
75
+ opacity: 1;
76
+ transform: translateY(0);
77
+ }
78
+ }
79
+
80
+ @keyframes kt-toast-out {
81
+ from {
82
+ opacity: 1;
83
+ }
84
+ to {
85
+ opacity: 0;
86
+ }
87
+ }
88
+
89
+ @keyframes kt-toast-progress-line {
90
+ from {
91
+ transform: scaleX(1);
92
+ }
93
+ to {
94
+ transform: scaleX(0);
95
+ }
96
+ }
97
+ }
@@ -33,6 +33,9 @@ export type KTToastPosition =
33
33
  | 'top-end'
34
34
  | 'top-center'
35
35
  | 'top-start'
36
+ | 'middle-end'
37
+ | 'middle-center'
38
+ | 'middle-start'
36
39
  | 'bottom-end'
37
40
  | 'bottom-center'
38
41
  | 'bottom-start';