nexa-ui-kit 0.10.0 → 0.11.1

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 (73) hide show
  1. package/dist/components/NAlert.js +49 -28
  2. package/dist/components/NAlert.nexa +12 -6
  3. package/dist/components/NAutocomplete.js +25 -20
  4. package/dist/components/NAutocomplete.nexa +3 -3
  5. package/dist/components/NAvatar.js +1 -1
  6. package/dist/components/NBadge.js +1 -1
  7. package/dist/components/NBottomSheet.js +17 -17
  8. package/dist/components/NBottomSheet.nexa +2 -2
  9. package/dist/components/NButton.js +59 -59
  10. package/dist/components/NButton.nexa +10 -9
  11. package/dist/components/NCard.js +2 -2
  12. package/dist/components/NCard.nexa +1 -1
  13. package/dist/components/NCheckbox.js +23 -19
  14. package/dist/components/NCheckbox.nexa +2 -1
  15. package/dist/components/NChips.js +13 -10
  16. package/dist/components/NChips.nexa +1 -1
  17. package/dist/components/NDataTable.js +360 -42
  18. package/dist/components/NDataTable.nexa +318 -10
  19. package/dist/components/NDatepicker.js +52 -43
  20. package/dist/components/NDatepicker.nexa +3 -3
  21. package/dist/components/NForm.js +1 -1
  22. package/dist/components/NFormField.js +1 -1
  23. package/dist/components/NInput.js +51 -41
  24. package/dist/components/NInput.nexa +4 -3
  25. package/dist/components/NInputNumber.js +18 -13
  26. package/dist/components/NInputNumber.nexa +2 -2
  27. package/dist/components/NModal.js +34 -28
  28. package/dist/components/NModal.nexa +4 -4
  29. package/dist/components/NMultiSelect.js +47 -38
  30. package/dist/components/NMultiSelect.nexa +3 -3
  31. package/dist/components/NPaginator.js +29 -21
  32. package/dist/components/NPaginator.nexa +4 -4
  33. package/dist/components/NPassword.js +61 -47
  34. package/dist/components/NPassword.nexa +5 -4
  35. package/dist/components/NProgressBar.js +5 -5
  36. package/dist/components/NProgressBar.nexa +4 -4
  37. package/dist/components/NRadio.js +1 -1
  38. package/dist/components/NScrollView.js +1 -1
  39. package/dist/components/NSelect.js +68 -63
  40. package/dist/components/NSelect.nexa +4 -4
  41. package/dist/components/NSkeleton.js +1 -1
  42. package/dist/components/NSwitch.js +1 -1
  43. package/dist/components/NTabs.js +1 -1
  44. package/dist/components/NTag.js +27 -24
  45. package/dist/components/NTag.nexa +6 -6
  46. package/dist/components/NToastContainer.js +65 -49
  47. package/dist/components/NToastContainer.nexa +6 -3
  48. package/dist/components/NTooltip.js +1 -1
  49. package/dist/components/NTreeMenu.js +74 -35
  50. package/dist/components/NTreeMenu.nexa +52 -15
  51. package/dist/styles/tokens.css +18 -0
  52. package/package.json +4 -4
  53. package/src/components/NAlert.nexa +12 -6
  54. package/src/components/NAutocomplete.nexa +3 -3
  55. package/src/components/NBottomSheet.nexa +2 -2
  56. package/src/components/NButton.nexa +10 -9
  57. package/src/components/NCard.nexa +1 -1
  58. package/src/components/NCheckbox.nexa +2 -1
  59. package/src/components/NChips.nexa +1 -1
  60. package/src/components/NDataTable.nexa +318 -10
  61. package/src/components/NDatepicker.nexa +3 -3
  62. package/src/components/NInput.nexa +4 -3
  63. package/src/components/NInputNumber.nexa +2 -2
  64. package/src/components/NModal.nexa +4 -4
  65. package/src/components/NMultiSelect.nexa +3 -3
  66. package/src/components/NPaginator.nexa +4 -4
  67. package/src/components/NPassword.nexa +5 -4
  68. package/src/components/NProgressBar.nexa +4 -4
  69. package/src/components/NSelect.nexa +4 -4
  70. package/src/components/NTag.nexa +6 -6
  71. package/src/components/NToastContainer.nexa +6 -3
  72. package/src/components/NTreeMenu.nexa +52 -15
  73. package/src/styles/tokens.css +18 -0
@@ -220,9 +220,9 @@ const onFocus = () => {
220
220
  <div class="n-inum">
221
221
  <label v-if="label" class="n-inum-label">{{ label }}</label>
222
222
  <div class="n-inum-wrap" :class="{ 'is-disabled': effectiveDisabled.value }">
223
- <button type="button" class="n-inum-btn n-inum-dec" :disabled="effectiveDisabled.value || readonly" aria-label="Decrement" @click="dec">−</button>
223
+ <button type="button" class="n-inum-btn n-inum-dec" :disabled="effectiveDisabled.value || readonly" aria-label="Decrement" @click="dec"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
224
224
  <input class="n-inum-input" type="text" :value="displayText.value" :placeholder="placeholder" :disabled="effectiveDisabled.value" :readonly="readonly" inputmode="decimal" autocomplete="off" @beforeinput="onBeforeInput" @keydown="onKeydown" @paste="onPaste" @input="onInput" @focus="onFocus" @blur="onBlur" />
225
- <button type="button" class="n-inum-btn n-inum-inc" :disabled="effectiveDisabled.value || readonly" aria-label="Increment" @click="inc">+</button>
225
+ <button type="button" class="n-inum-btn n-inum-inc" :disabled="effectiveDisabled.value || readonly" aria-label="Increment" @click="inc"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
226
226
  </div>
227
227
  </div>
228
228
  </template>
@@ -100,11 +100,11 @@ onUnmounted(() => {
100
100
  @keydown="handleKeydown"
101
101
  >
102
102
  <div v-if="title || $slots.header" class="n-modal-header" :class="{ 'close-left': closeLeft }">
103
- <button v-if="closable && closeLeft" class="n-modal-close" @click="close" aria-label="Close">&times;</button>
103
+ <button v-if="closable && closeLeft" class="n-modal-close" @click="close" aria-label="Close"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button>
104
104
  <slot name="header">
105
105
  <h3>{{ title }}</h3>
106
106
  </slot>
107
- <button v-if="closable && !closeLeft" class="n-modal-close" @click="close" aria-label="Close">&times;</button>
107
+ <button v-if="closable && !closeLeft" class="n-modal-close" @click="close" aria-label="Close"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button>
108
108
  </div>
109
109
  <div class="n-modal-content">
110
110
  <slot />
@@ -155,7 +155,7 @@ onUnmounted(() => {
155
155
  box-shadow: var(--n-shadow-xl);
156
156
  transform: scale(0.9) translateY(20px);
157
157
  opacity: 0;
158
- transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
158
+ transition: all var(--n-transition-spring);
159
159
  overflow: hidden;
160
160
  outline: none;
161
161
  max-height: 85vh;
@@ -216,7 +216,7 @@ onUnmounted(() => {
216
216
 
217
217
  .n-modal-footer {
218
218
  padding: var(--n-space-5) var(--n-space-8);
219
- background: rgba(0, 0, 0, 0.15);
219
+ background: var(--n-color-glass);
220
220
  border-top: 1px solid var(--n-color-border);
221
221
  display: flex;
222
222
  justify-content: flex-end;
@@ -65,10 +65,10 @@ onBeforeUnmount(() => close())
65
65
  <div class="n-ms-trigger" role="combobox" tabindex="0" :aria-expanded="isOpen.value" @click="toggleOpen" @keydown="onKeydown">
66
66
  <div v-if="selectedLabels.value.length > 0" class="n-ms-chips"><div v-for="c in selectedLabels.value.slice(0, maxChips)" :key="String(c.value)" class="n-ms-chip"><span class="n-ms-chip-label">{{ c.label }}</span><button type="button" class="n-ms-chip-remove" aria-label="Remove" tabindex="-1" :disabled="effectiveDisabled.value" @click="removeChip(c.value, $event)">✕</button></div><span v-if="selectedLabels.value.length > maxChips" class="n-ms-more">+{{ selectedLabels.value.length - maxChips }}</span></div>
67
67
  <span v-else class="n-ms-placeholder">{{ placeholder }}</span>
68
- <div class="n-ms-actions"><button v-if="clearable && selectedLabels.value.length > 0" type="button" class="n-ms-clear" tabindex="-1" @click.stop="clear">✕</button><span class="n-ms-arrow">▾</span></div>
68
+ <div class="n-ms-actions"><button v-if="clearable && selectedLabels.value.length > 0" type="button" class="n-ms-clear" tabindex="-1" @click.stop="clear" aria-label="Clear"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button><span class="n-ms-arrow"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></span></div>
69
69
  </div>
70
- <div v-if="isOpen.value && appendTo !== 'body'" class="n-ms-dropdown"><div v-if="searchable" class="n-ms-search"><input class="n-ms-search-input" :value="queryDraft.value" placeholder="Search..." @input="onSearchInput" @keydown.stop="onKeydown" /></div><div class="n-ms-options"><button v-for="(opt, i) in filteredOptions.value" :key="String(opt.value) + ':' + i" type="button" class="n-ms-option" :data-ms-opt="String(opt.value)" :class="{ 'is-selected': selectedSet.value.has(opt.value), 'is-focused': i === focusedIndex.value, 'is-disabled': opt.disabled }" :disabled="opt.disabled" @mouseenter="focusedIndex.value = i" @click="toggleValue(opt.value)"><span class="n-ms-check">{{ selectedSet.value.has(opt.value) ? '✓' : '' }}</span><span class="n-ms-option-label">{{ opt.label }}</span></button><div v-if="filteredOptions.value.length === 0" class="n-ms-empty">No options</div></div></div>
71
- <Teleport to="body"><div v-if="isOpen.value && appendTo === 'body'" class="n-ms-dropdown" :class="{ 'is-top': resolvedPlacement.value === 'top' }" :data-ms-popup="instanceId" :style="popupStyle.value"><div v-if="searchable" class="n-ms-search"><input class="n-ms-search-input" :value="queryDraft.value" placeholder="Search..." @input="onSearchInput" @keydown.stop="onKeydown" /></div><div class="n-ms-options"><button v-for="(opt, i) in filteredOptions.value" :key="String(opt.value) + ':' + i" type="button" class="n-ms-option" :data-ms-opt="String(opt.value)" :class="{ 'is-selected': selectedSet.value.has(opt.value), 'is-focused': i === focusedIndex.value, 'is-disabled': opt.disabled }" :disabled="opt.disabled" @mouseenter="focusedIndex.value = i" @click="toggleValue(opt.value)"><span class="n-ms-check">{{ selectedSet.value.has(opt.value) ? '✓' : '' }}</span><span class="n-ms-option-label">{{ opt.label }}</span></button><div v-if="filteredOptions.value.length === 0" class="n-ms-empty">No options</div></div></div></Teleport>
70
+ <div v-if="isOpen.value && appendTo !== 'body'" class="n-ms-dropdown"><div v-if="searchable" class="n-ms-search"><input class="n-ms-search-input" :value="queryDraft.value" placeholder="Search..." @input="onSearchInput" @keydown.stop="onKeydown" /></div><div class="n-ms-options"><button v-for="(opt, i) in filteredOptions.value" :key="String(opt.value) + ':' + i" type="button" class="n-ms-option" :data-ms-opt="String(opt.value)" :class="{ 'is-selected': selectedSet.value.has(opt.value), 'is-focused': i === focusedIndex.value, 'is-disabled': opt.disabled }" :disabled="opt.disabled" @mouseenter="focusedIndex.value = i" @click="toggleValue(opt.value)"><span class="n-ms-check"><svg v-if="selectedSet.value.has(opt.value)" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg></span><span class="n-ms-option-label">{{ opt.label }}</span></button><div v-if="filteredOptions.value.length === 0" class="n-ms-empty">No options</div></div></div>
71
+ <Teleport to="body"><div v-if="isOpen.value && appendTo === 'body'" class="n-ms-dropdown" :class="{ 'is-top': resolvedPlacement.value === 'top' }" :data-ms-popup="instanceId" :style="popupStyle.value"><div v-if="searchable" class="n-ms-search"><input class="n-ms-search-input" :value="queryDraft.value" placeholder="Search..." @input="onSearchInput" @keydown.stop="onKeydown" /></div><div class="n-ms-options"><button v-for="(opt, i) in filteredOptions.value" :key="String(opt.value) + ':' + i" type="button" class="n-ms-option" :data-ms-opt="String(opt.value)" :class="{ 'is-selected': selectedSet.value.has(opt.value), 'is-focused': i === focusedIndex.value, 'is-disabled': opt.disabled }" :disabled="opt.disabled" @mouseenter="focusedIndex.value = i" @click="toggleValue(opt.value)"><span class="n-ms-check"><svg v-if="selectedSet.value.has(opt.value)" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg></span><span class="n-ms-option-label">{{ opt.label }}</span></button><div v-if="filteredOptions.value.length === 0" class="n-ms-empty">No options</div></div></div></Teleport>
72
72
  </div>
73
73
  </template>
74
74
 
@@ -58,11 +58,11 @@ const end = computed(() => Math.min((safeFirst.value || 0) + (safeRows.value ||
58
58
  <span class="n-paginator-report">{{ start.value }}-{{ end.value }} of {{ totalRecords }}</span>
59
59
  </div>
60
60
  <div class="n-paginator-center">
61
- <button type="button" class="n-pg-btn" :disabled="!canPrev.value" @click="firstPage">«</button>
62
- <button type="button" class="n-pg-btn" :disabled="!canPrev.value" @click="prev">‹</button>
61
+ <button type="button" class="n-pg-btn" :disabled="!canPrev.value" @click="firstPage" aria-label="First page"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M11 17l-5-5 5-5M18 17l-5-5 5-5"/></svg></button>
62
+ <button type="button" class="n-pg-btn" :disabled="!canPrev.value" @click="prev" aria-label="Previous page"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
63
63
  <span class="n-pg-page">{{ page.value + 1 }} / {{ pageCount.value }}</span>
64
- <button type="button" class="n-pg-btn" :disabled="!canNext.value" @click="next">›</button>
65
- <button type="button" class="n-pg-btn" :disabled="!canNext.value" @click="lastPage">»</button>
64
+ <button type="button" class="n-pg-btn" :disabled="!canNext.value" @click="next" aria-label="Next page"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg></button>
65
+ <button type="button" class="n-pg-btn" :disabled="!canNext.value" @click="lastPage" aria-label="Last page"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M13 17l5-5-5-5M6 17l5-5-5-5"/></svg></button>
66
66
  </div>
67
67
  <div class="n-paginator-right">
68
68
  <select class="n-pg-select" :value="rows" @change="changeRows">
@@ -123,9 +123,10 @@ const toggle = (e) => {
123
123
  @input="onInput"
124
124
  />
125
125
  <div class="n-password-actions">
126
- <button v-if="clearable && modelValue" type="button" class="n-password-action" :disabled="disabled || readonly" aria-label="Limpiar" @click="clear">✕</button>
126
+ <button v-if="clearable && modelValue" type="button" class="n-password-action" :disabled="disabled || readonly" aria-label="Limpiar" @click="clear"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button>
127
127
  <button v-if="toggleMask" type="button" class="n-password-action" :aria-pressed="(!masked.value).toString()" :disabled="disabled" :aria-label="masked.value ? 'Mostrar contraseña' : 'Ocultar contraseña'" @click="toggle">
128
- {{ masked.value ? '👁' : '🙈' }}
128
+ <svg v-if="masked.value" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
129
+ <svg v-else viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/><path d="M23 1L1 23"/></svg>
129
130
  </button>
130
131
  </div>
131
132
  </div>
@@ -143,7 +144,7 @@ const toggle = (e) => {
143
144
 
144
145
  <ul v-if="showRequirements && validate && modelValue" class="n-password-req" :id="inputId.value + '-requirements'">
145
146
  <li v-for="r in requirements.value" :key="r.key" class="n-password-req-item" :class="{ 'is-ok': r.ok }">
146
- <span class="n-password-req-icon">{{ r.ok ? '✓' : '•' }}</span>
147
+ <span class="n-password-req-icon"><svg v-if="r.ok" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg><svg v-else viewBox="0 0 24 24" width="12" height="12" fill="currentColor" focusable="false" aria-hidden="true"><circle cx="12" cy="12" r="4"/></svg></span>
147
148
  <span>{{ r.label }}</span>
148
149
  </li>
149
150
  </ul>
@@ -156,7 +157,7 @@ const toggle = (e) => {
156
157
  .n-password-wrapper { position:relative; display:flex; align-items:center; background: var(--n-color-bg); border: 1px solid var(--n-color-border); border-radius: var(--n-radius-md); transition: all var(--n-transition-normal); }
157
158
  .n-password-wrapper:focus-within { border-color: var(--n-color-primary); box-shadow: 0 0 0 3px var(--n-color-primary-light); background: var(--n-color-surface); }
158
159
  .n-password-wrapper.has-error { border-color: var(--n-color-danger); }
159
- .n-password-wrapper.has-error:focus-within { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.25); }
160
+ .n-password-wrapper.has-error:focus-within { box-shadow: 0 0 0 3px var(--n-color-danger-light); }
160
161
  .n-password-input { width:100%; background:transparent; border:none; outline:none; color: var(--n-color-text); padding: 0.75rem 2.75rem 0.75rem 1rem; font-size: var(--n-text-base); font-family: inherit; }
161
162
  .n-password-input::placeholder { color: var(--n-color-text-muted); }
162
163
  .n-password-actions { position:absolute; right: 0.5rem; display:flex; align-items:center; gap: 0.15rem; }
@@ -51,7 +51,7 @@ const percentage = computed(() => Math.min(100, Math.max(0, (props.value / props
51
51
  .n-progress-bar {
52
52
  height: 100%;
53
53
  border-radius: var(--n-radius-full);
54
- transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
54
+ transition: width var(--n-transition-slow);
55
55
  display: flex;
56
56
  align-items: center;
57
57
  justify-content: flex-end;
@@ -89,11 +89,11 @@ const percentage = computed(() => Math.min(100, Math.max(0, (props.value / props
89
89
  .is-striped .n-progress-bar {
90
90
  background-image: linear-gradient(
91
91
  45deg,
92
- rgba(255, 255, 255, 0.12) 25%,
92
+ var(--n-color-stripe) 25%,
93
93
  transparent 25%,
94
94
  transparent 50%,
95
- rgba(255, 255, 255, 0.12) 50%,
96
- rgba(255, 255, 255, 0.12) 75%,
95
+ var(--n-color-stripe) 50%,
96
+ var(--n-color-stripe) 75%,
97
97
  transparent 75%,
98
98
  transparent
99
99
  );
@@ -200,8 +200,8 @@ onBeforeUnmount(() => {
200
200
  {{ placeholder }}
201
201
  </span>
202
202
  <div class="n-select-trigger-actions">
203
- <button v-if="clearable && modelValue" type="button" class="n-select-clear" @click.stop="clear" tabindex="-1">✕</button>
204
- <span class="n-select-arrow">▾</span>
203
+ <button v-if="clearable && modelValue" type="button" class="n-select-clear" @click.stop="clear" tabindex="-1" aria-label="Clear"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button>
204
+ <span class="n-select-arrow"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></span>
205
205
  </div>
206
206
  </div>
207
207
  <div v-if="isOpen.value && appendTo !== 'body'" class="n-select-dropdown">
@@ -361,7 +361,7 @@ onBeforeUnmount(() => {
361
361
  box-shadow: var(--n-shadow-lg);
362
362
  z-index: var(--n-z-dropdown);
363
363
  overflow: hidden;
364
- animation: n-dropdown-in 0.2s cubic-bezier(0, 1, 0, 1);
364
+ animation: n-dropdown-in 0.2s ease-out;
365
365
  }
366
366
 
367
367
  @keyframes n-dropdown-in {
@@ -370,7 +370,7 @@ onBeforeUnmount(() => {
370
370
  }
371
371
 
372
372
  .n-select-dropdown.is-top {
373
- animation: n-dropdown-in-top 0.2s cubic-bezier(0, 1, 0, 1);
373
+ animation: n-dropdown-in-top 0.2s ease-out;
374
374
  }
375
375
 
376
376
  @keyframes n-dropdown-in-top {
@@ -12,7 +12,7 @@ const emit = defineEmits(['close'])
12
12
  <template>
13
13
  <span :class="['n-tag', `n-tag-${variant}`, `n-tag-${size}`, rounded ? 'is-rounded' : '']">
14
14
  <slot />
15
- <button v-if="closable" class="n-tag-close" @click="emit('close')">&times;</button>
15
+ <button v-if="closable" class="n-tag-close" @click="emit('close')" aria-label="Close"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button>
16
16
  </span>
17
17
  </template>
18
18
 
@@ -40,31 +40,31 @@ const emit = defineEmits(['close'])
40
40
 
41
41
  .n-tag-primary {
42
42
  background: var(--n-color-primary-light);
43
- border-color: rgba(59, 130, 246, 0.25);
43
+ border-color: var(--n-color-primary-border);
44
44
  color: var(--n-color-primary);
45
45
  }
46
46
 
47
47
  .n-tag-success {
48
48
  background: var(--n-color-success-light);
49
- border-color: rgba(16, 185, 129, 0.25);
49
+ border-color: var(--n-color-success-border);
50
50
  color: var(--n-color-success);
51
51
  }
52
52
 
53
53
  .n-tag-danger {
54
54
  background: var(--n-color-danger-light);
55
- border-color: rgba(239, 68, 68, 0.25);
55
+ border-color: var(--n-color-danger-border);
56
56
  color: var(--n-color-danger);
57
57
  }
58
58
 
59
59
  .n-tag-warning {
60
60
  background: var(--n-color-warning-light);
61
- border-color: rgba(245, 158, 11, 0.25);
61
+ border-color: var(--n-color-warning-border);
62
62
  color: var(--n-color-warning);
63
63
  }
64
64
 
65
65
  .n-tag-info {
66
66
  background: var(--n-color-info-light);
67
- border-color: rgba(6, 182, 212, 0.25);
67
+ border-color: var(--n-color-info-border);
68
68
  color: var(--n-color-info);
69
69
  }
70
70
 
@@ -22,10 +22,13 @@ const positionClass = `is-${props.position.replace('-', '-')}`
22
22
  >
23
23
  <div class="n-toast-body">
24
24
  <span class="n-toast-icon">
25
- {{ toast.type === 'success' ? '✓' : toast.type === 'error' ? '✕' : toast.type === 'warning' ? '⚡' : 'ℹ' }}
25
+ <svg v-if="toast.type === 'success'" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
26
+ <svg v-else-if="toast.type === 'error'" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg>
27
+ <svg v-else-if="toast.type === 'warning'" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M12 9v4"/><path d="M12 17h.01"/><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>
28
+ <svg v-else viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
26
29
  </span>
27
30
  <span class="n-toast-message">{{ toast.message }}</span>
28
- <button class="n-toast-dismiss" @click.stop="remove(toast.id)" aria-label="Dismiss">&times;</button>
31
+ <button class="n-toast-dismiss" @click.stop="remove(toast.id)" aria-label="Dismiss"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg></button>
29
32
  </div>
30
33
  <div v-if="toast.duration > 0" class="n-toast-progress" :style="{ animationDuration: toast.duration + 'ms' }"></div>
31
34
  </div>
@@ -87,7 +90,7 @@ const positionClass = `is-${props.position.replace('-', '-')}`
87
90
  overflow: hidden;
88
91
  box-shadow: var(--n-shadow-lg);
89
92
  cursor: pointer;
90
- animation: n-toast-in 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
93
+ animation: n-toast-in var(--n-transition-spring);
91
94
  transition: all var(--n-transition-fast);
92
95
  }
93
96
 
@@ -59,7 +59,7 @@ const toggle = (id, e) => {
59
59
  class="n-tree-toggle"
60
60
  :class="{ 'is-expanded': isExpanded(item.id) }"
61
61
  @click="toggle(item.id, $event)"
62
- >▶</button>
62
+ ><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg></button>
63
63
  <span v-else class="n-tree-toggle-placeholder"></span>
64
64
  <span v-if="item.icon" class="n-tree-icon">{{ item.icon }}</span>
65
65
  <span class="n-tree-label">{{ item.label }}</span>
@@ -77,18 +77,44 @@ const toggle = (id, e) => {
77
77
 
78
78
  .n-tree-item {
79
79
  --n-tree-depth: 0;
80
+ position: relative;
81
+ }
82
+
83
+ /* Líneas conectoras verticales */
84
+ .n-tree-item:not([style*="--n-tree-depth: 0"])::before {
85
+ content: '';
86
+ position: absolute;
87
+ left: calc(0.5rem + (var(--n-tree-depth, 0) - 1) * 1.25rem + 0.5rem);
88
+ top: 0;
89
+ bottom: 0;
90
+ width: 1px;
91
+ background: var(--n-color-border);
92
+ opacity: 0.5;
80
93
  }
81
94
 
82
95
  .n-tree-row {
83
96
  display: flex;
84
97
  align-items: center;
85
- gap: 0.35rem;
86
- padding: 0.4rem 0.5rem;
87
- padding-left: calc(0.5rem + var(--n-tree-depth, 0) * 1.25rem);
98
+ gap: 0.5rem;
99
+ padding: 0.5rem 0.75rem;
100
+ padding-left: calc(0.75rem + var(--n-tree-depth, 0) * 1.25rem);
88
101
  border-radius: var(--n-radius-sm);
89
102
  cursor: pointer;
90
103
  transition: all var(--n-transition-fast);
91
104
  color: var(--n-color-text);
105
+ position: relative;
106
+ }
107
+
108
+ /* Línea conectora horizontal */
109
+ .n-tree-item:not([style*="--n-tree-depth: 0"]) .n-tree-row::before {
110
+ content: '';
111
+ position: absolute;
112
+ left: calc(0.75rem + (var(--n-tree-depth, 0) - 1) * 1.25rem + 0.5rem);
113
+ top: 50%;
114
+ width: 0.75rem;
115
+ height: 1px;
116
+ background: var(--n-color-border);
117
+ opacity: 0.5;
92
118
  }
93
119
 
94
120
  .n-tree-row:hover {
@@ -102,33 +128,43 @@ const toggle = (id, e) => {
102
128
  }
103
129
 
104
130
  .n-tree-toggle {
105
- background: none;
106
- border: none;
107
- color: var(--n-color-text-muted);
131
+ background: var(--n-color-glass);
132
+ border: 1px solid var(--n-color-border);
133
+ border-radius: var(--n-radius-sm);
134
+ color: var(--n-color-text-secondary);
108
135
  cursor: pointer;
109
- font-size: 0.6rem;
110
- width: 16px;
111
- height: 16px;
136
+ width: 20px;
137
+ height: 20px;
112
138
  display: flex;
113
139
  align-items: center;
114
140
  justify-content: center;
115
141
  padding: 0;
116
- transition: transform var(--n-transition-fast);
142
+ transition: all var(--n-transition-fast);
117
143
  flex-shrink: 0;
118
144
  }
119
145
 
120
- .n-tree-toggle.is-expanded {
146
+ .n-tree-toggle:hover {
147
+ background: var(--n-color-primary-light);
148
+ border-color: var(--n-color-primary);
149
+ color: var(--n-color-primary);
150
+ }
151
+
152
+ .n-tree-toggle svg {
153
+ transition: transform var(--n-transition-fast);
154
+ }
155
+
156
+ .n-tree-toggle.is-expanded svg {
121
157
  transform: rotate(90deg);
122
158
  }
123
159
 
124
160
  .n-tree-toggle-placeholder {
125
- width: 16px;
126
- height: 16px;
161
+ width: 20px;
162
+ height: 20px;
127
163
  flex-shrink: 0;
128
164
  }
129
165
 
130
166
  .n-tree-icon {
131
- font-size: 1rem;
167
+ font-size: 1.1rem;
132
168
  flex-shrink: 0;
133
169
  }
134
170
 
@@ -138,5 +174,6 @@ const toggle = (id, e) => {
138
174
  overflow: hidden;
139
175
  text-overflow: ellipsis;
140
176
  white-space: nowrap;
177
+ font-weight: var(--n-weight-medium);
141
178
  }
142
179
  </style>
@@ -44,6 +44,15 @@
44
44
  --n-color-glass-border: rgba(255, 255, 255, 0.08);
45
45
  --n-color-glass-hover: rgba(255, 255, 255, 0.08);
46
46
 
47
+ --n-color-primary-border: rgba(59, 130, 246, 0.2);
48
+ --n-color-success-border: rgba(16, 185, 129, 0.2);
49
+ --n-color-warning-border: rgba(245, 158, 11, 0.2);
50
+ --n-color-danger-border: rgba(239, 68, 68, 0.2);
51
+ --n-color-info-border: rgba(6, 182, 212, 0.2);
52
+
53
+ --n-color-stripe: rgba(255, 255, 255, 0.12);
54
+ --n-shadow-top-lg: 0 -10px 25px -5px rgba(0, 0, 0, 0.3);
55
+
47
56
  /* ─── Typography ─── */
48
57
  --n-font-sans: 'Outfit', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
49
58
  --n-font-mono: 'JetBrains Mono', 'Fira Code', monospace;
@@ -158,6 +167,15 @@
158
167
  --n-color-glass-border: rgba(0, 0, 0, 0.06);
159
168
  --n-color-glass-hover: rgba(255, 255, 255, 0.8);
160
169
 
170
+ --n-color-primary-border: rgba(37, 99, 235, 0.2);
171
+ --n-color-success-border: rgba(5, 150, 105, 0.2);
172
+ --n-color-warning-border: rgba(217, 119, 6, 0.2);
173
+ --n-color-danger-border: rgba(220, 38, 38, 0.2);
174
+ --n-color-info-border: rgba(8, 145, 178, 0.2);
175
+
176
+ --n-color-stripe: rgba(0, 0, 0, 0.08);
177
+ --n-shadow-top-lg: 0 -10px 25px -5px rgba(0, 0, 0, 0.2);
178
+
161
179
  --n-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
162
180
  --n-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.06);
163
181
  --n-shadow-lg: 0 10px 25px -5px rgba(0, 0, 0, 0.08);