snice 1.14.3 → 2.1.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.
Files changed (185) hide show
  1. package/bin/templates/base/tsconfig.json +5 -4
  2. package/components/accordion/demo.html +403 -0
  3. package/components/accordion/snice-accordion-item.css +85 -0
  4. package/components/accordion/snice-accordion-item.ts +226 -0
  5. package/components/accordion/snice-accordion.css +31 -0
  6. package/components/accordion/snice-accordion.ts +182 -0
  7. package/components/accordion/snice-accordion.types.ts +32 -0
  8. package/components/alert/demo.html +445 -0
  9. package/components/alert/snice-alert.css +195 -0
  10. package/components/alert/snice-alert.ts +141 -0
  11. package/components/alert/snice-alert.types.ts +12 -0
  12. package/components/avatar/demo.html +598 -0
  13. package/components/avatar/snice-avatar.css +131 -0
  14. package/components/avatar/snice-avatar.ts +136 -0
  15. package/components/avatar/snice-avatar.types.ts +13 -0
  16. package/components/badge/demo.html +523 -0
  17. package/components/badge/snice-badge.css +161 -0
  18. package/components/badge/snice-badge.ts +117 -0
  19. package/components/badge/snice-badge.types.ts +16 -0
  20. package/components/breadcrumbs/demo.html +404 -0
  21. package/components/breadcrumbs/snice-breadcrumbs.css +133 -0
  22. package/components/breadcrumbs/snice-breadcrumbs.ts +191 -0
  23. package/components/breadcrumbs/snice-breadcrumbs.types.ts +26 -0
  24. package/components/breadcrumbs/snice-crumb.ts +26 -0
  25. package/components/button/demo.html +42 -0
  26. package/components/button/snice-button.css +230 -0
  27. package/components/button/snice-button.ts +169 -0
  28. package/components/button/snice-button.types.ts +25 -0
  29. package/components/card/demo.html +525 -0
  30. package/components/card/snice-card.css +140 -0
  31. package/components/card/snice-card.ts +102 -0
  32. package/components/card/snice-card.types.ts +10 -0
  33. package/components/checkbox/demo.html +253 -0
  34. package/components/checkbox/snice-checkbox.css +164 -0
  35. package/components/checkbox/snice-checkbox.ts +223 -0
  36. package/components/checkbox/snice-checkbox.types.ts +22 -0
  37. package/components/chip/demo.html +383 -0
  38. package/components/chip/snice-chip.css +195 -0
  39. package/components/chip/snice-chip.ts +139 -0
  40. package/components/chip/snice-chip.types.ts +15 -0
  41. package/components/date-picker/README.md +233 -0
  42. package/components/date-picker/demo.html +191 -0
  43. package/components/date-picker/snice-date-picker.css +330 -0
  44. package/components/date-picker/snice-date-picker.ts +777 -0
  45. package/components/date-picker/snice-date-picker.types.ts +83 -0
  46. package/components/divider/demo.html +233 -0
  47. package/components/divider/snice-divider.css +155 -0
  48. package/components/divider/snice-divider.ts +69 -0
  49. package/components/divider/snice-divider.types.ts +15 -0
  50. package/components/drawer/demo.html +328 -0
  51. package/components/drawer/snice-drawer.css +476 -0
  52. package/components/drawer/snice-drawer.ts +287 -0
  53. package/components/drawer/snice-drawer.types.ts +17 -0
  54. package/components/global.d.ts +14 -0
  55. package/components/input/demo.html +303 -0
  56. package/components/input/snice-input.css +257 -0
  57. package/components/input/snice-input.ts +442 -0
  58. package/components/input/snice-input.types.ts +59 -0
  59. package/components/input/test.html +77 -0
  60. package/components/layout/README.md +260 -0
  61. package/components/layout/demo.html +538 -0
  62. package/components/layout/snice-layout-blog.css +129 -0
  63. package/components/layout/snice-layout-blog.ts +48 -0
  64. package/components/layout/snice-layout-card.css +104 -0
  65. package/components/layout/snice-layout-card.ts +35 -0
  66. package/components/layout/snice-layout-centered.css +51 -0
  67. package/components/layout/snice-layout-centered.ts +22 -0
  68. package/components/layout/snice-layout-dashboard.css +98 -0
  69. package/components/layout/snice-layout-dashboard.ts +45 -0
  70. package/components/layout/snice-layout-fullscreen.css +72 -0
  71. package/components/layout/snice-layout-fullscreen.ts +34 -0
  72. package/components/layout/snice-layout-landing.css +92 -0
  73. package/components/layout/snice-layout-landing.ts +47 -0
  74. package/components/layout/snice-layout-minimal.css +16 -0
  75. package/components/layout/snice-layout-minimal.ts +19 -0
  76. package/components/layout/snice-layout-sidebar.css +117 -0
  77. package/components/layout/snice-layout-sidebar.ts +48 -0
  78. package/components/layout/snice-layout-split.css +103 -0
  79. package/components/layout/snice-layout-split.ts +29 -0
  80. package/components/layout/snice-layout.css +72 -0
  81. package/components/layout/snice-layout.ts +35 -0
  82. package/components/layout/snice-layout.types.ts +5 -0
  83. package/components/login/demo-auth-controller.ts +185 -0
  84. package/components/login/demo.html +470 -0
  85. package/components/login/snice-login.css +204 -0
  86. package/components/login/snice-login.ts +337 -0
  87. package/components/login/snice-login.types.ts +34 -0
  88. package/components/modal/demo.html +291 -0
  89. package/components/modal/snice-modal.css +203 -0
  90. package/components/modal/snice-modal.ts +233 -0
  91. package/components/modal/snice-modal.types.ts +21 -0
  92. package/components/pagination/demo.html +395 -0
  93. package/components/pagination/snice-pagination.ts +333 -0
  94. package/components/pagination/snice-pagination.types.ts +21 -0
  95. package/components/progress/demo.html +510 -0
  96. package/components/progress/snice-progress.css +267 -0
  97. package/components/progress/snice-progress.ts +247 -0
  98. package/components/progress/snice-progress.types.ts +19 -0
  99. package/components/radio/demo.html +287 -0
  100. package/components/radio/snice-radio.css +171 -0
  101. package/components/radio/snice-radio.ts +218 -0
  102. package/components/radio/snice-radio.types.ts +21 -0
  103. package/components/select/demo.html +511 -0
  104. package/components/select/snice-option.ts +52 -0
  105. package/components/select/snice-option.types.ts +14 -0
  106. package/components/select/snice-select.css +392 -0
  107. package/components/select/snice-select.ts +796 -0
  108. package/components/select/snice-select.types.ts +55 -0
  109. package/components/skeleton/demo.html +514 -0
  110. package/components/skeleton/snice-skeleton.css +109 -0
  111. package/components/skeleton/snice-skeleton.ts +126 -0
  112. package/components/skeleton/snice-skeleton.types.ts +11 -0
  113. package/components/switch/demo.html +284 -0
  114. package/components/switch/snice-switch.css +221 -0
  115. package/components/switch/snice-switch.ts +229 -0
  116. package/components/switch/snice-switch.types.ts +23 -0
  117. package/components/symbols.ts +23 -0
  118. package/components/table/demo-table-controller.ts +100 -0
  119. package/components/table/demo.html +480 -0
  120. package/components/table/snice-cell-boolean.ts +112 -0
  121. package/components/table/snice-cell-date.ts +210 -0
  122. package/components/table/snice-cell-duration.ts +91 -0
  123. package/components/table/snice-cell-filesize.ts +90 -0
  124. package/components/table/snice-cell-number.ts +165 -0
  125. package/components/table/snice-cell-progress.ts +83 -0
  126. package/components/table/snice-cell-rating.ts +82 -0
  127. package/components/table/snice-cell-sparkline.ts +253 -0
  128. package/components/table/snice-cell-text.ts +125 -0
  129. package/components/table/snice-cell.css +296 -0
  130. package/components/table/snice-cell.ts +473 -0
  131. package/components/table/snice-column.ts +353 -0
  132. package/components/table/snice-header.css +243 -0
  133. package/components/table/snice-header.ts +261 -0
  134. package/components/table/snice-progress.ts +66 -0
  135. package/components/table/snice-rating.ts +45 -0
  136. package/components/table/snice-row.css +255 -0
  137. package/components/table/snice-row.ts +331 -0
  138. package/components/table/snice-table.css +241 -0
  139. package/components/table/snice-table.ts +737 -0
  140. package/components/table/snice-table.types.ts +158 -0
  141. package/components/tabs/demo.html +487 -0
  142. package/components/tabs/snice-tab-panel.css +264 -0
  143. package/components/tabs/snice-tab-panel.ts +47 -0
  144. package/components/tabs/snice-tab.css +96 -0
  145. package/components/tabs/snice-tab.ts +65 -0
  146. package/components/tabs/snice-tabs.css +189 -0
  147. package/components/tabs/snice-tabs.ts +332 -0
  148. package/components/tabs/snice-tabs.types.ts +28 -0
  149. package/components/theme/theme.css +234 -0
  150. package/components/toast/demo.html +329 -0
  151. package/components/toast/snice-toast-container.ts +256 -0
  152. package/components/toast/snice-toast.css +213 -0
  153. package/components/toast/snice-toast.ts +276 -0
  154. package/components/toast/snice-toast.types.ts +35 -0
  155. package/components/tooltip/demo.html +350 -0
  156. package/components/tooltip/snice-tooltip-portal.css +79 -0
  157. package/components/tooltip/snice-tooltip.css +117 -0
  158. package/components/tooltip/snice-tooltip.ts +612 -0
  159. package/components/tooltip/snice-tooltip.types.ts +32 -0
  160. package/components/transitions.ts +94 -0
  161. package/components/tsconfig.json +18 -0
  162. package/dist/index.cjs +441 -329
  163. package/dist/index.cjs.map +1 -1
  164. package/dist/index.cjs.min.map +1 -1
  165. package/dist/index.esm.js +441 -329
  166. package/dist/index.esm.js.map +1 -1
  167. package/dist/index.esm.min.js +3 -3
  168. package/dist/index.esm.min.js.map +1 -1
  169. package/dist/index.iife.js +441 -329
  170. package/dist/index.iife.js.map +1 -1
  171. package/dist/index.iife.min.js +3 -3
  172. package/dist/index.iife.min.js.map +1 -1
  173. package/dist/symbols.esm.js +1 -1
  174. package/dist/transitions.esm.js +1 -1
  175. package/dist/types/controller.d.ts +1 -1
  176. package/dist/types/element.d.ts +10 -10
  177. package/dist/types/events.d.ts +2 -2
  178. package/dist/types/index.d.ts +1 -1
  179. package/dist/types/observe.d.ts +1 -1
  180. package/dist/types/request-response.d.ts +2 -3
  181. package/dist/types/router.d.ts +1 -1
  182. package/package.json +9 -3
  183. package/dist/index.cjs.min +0 -15
  184. package/dist/symbols.cjs +0 -103
  185. package/dist/transitions.cjs +0 -219
@@ -0,0 +1,117 @@
1
+ /* Sidebar Layout */
2
+ :host {
3
+ display: block;
4
+ font-family: var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
5
+ }
6
+
7
+ .layout {
8
+ display: grid;
9
+ grid-template-columns: 250px 1fr;
10
+ min-height: 100vh;
11
+ transition: grid-template-columns var(--snice-transition-normal, 0.25s) ease;
12
+ }
13
+
14
+ :host([collapsed]) .layout {
15
+ grid-template-columns: 60px 1fr;
16
+ }
17
+
18
+ .sidebar {
19
+ background: var(--snice-color-background, white);
20
+ border-right: 1px solid var(--snice-color-border, #d1d5db);
21
+ display: flex;
22
+ flex-direction: column;
23
+ overflow: hidden;
24
+ }
25
+
26
+ .sidebar-header {
27
+ padding: var(--snice-spacing-lg, 2rem) var(--snice-spacing-md, 1rem);
28
+ border-bottom: 1px solid var(--snice-color-border, #d1d5db);
29
+ }
30
+
31
+ .sidebar-header h2 {
32
+ margin: 0;
33
+ font-size: var(--snice-font-size-lg, 1.125rem);
34
+ font-weight: var(--snice-font-weight-semibold, 600);
35
+ color: var(--snice-color-text, #374151);
36
+ white-space: nowrap;
37
+ overflow: hidden;
38
+ text-overflow: ellipsis;
39
+ }
40
+
41
+ :host([collapsed]) .sidebar-header {
42
+ padding: var(--snice-spacing-md, 1rem) var(--snice-spacing-xs, 0.5rem);
43
+ text-align: center;
44
+ }
45
+
46
+ .sidebar-nav {
47
+ flex: 1;
48
+ padding: var(--snice-spacing-md, 1rem);
49
+ overflow-y: auto;
50
+ }
51
+
52
+ .content-area {
53
+ display: grid;
54
+ grid-template-rows: auto 1fr auto;
55
+ background: var(--snice-color-background-secondary, #f8fafc);
56
+ }
57
+
58
+ .header {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: var(--snice-spacing-md, 1rem);
62
+ padding: var(--snice-spacing-md, 1rem) var(--snice-spacing-lg, 2rem);
63
+ background: var(--snice-color-background, white);
64
+ border-bottom: 1px solid var(--snice-color-border, #d1d5db);
65
+ }
66
+
67
+ .sidebar-toggle {
68
+ background: none;
69
+ border: none;
70
+ color: var(--snice-color-text-secondary, #6b7280);
71
+ cursor: pointer;
72
+ padding: var(--snice-spacing-xs, 0.5rem);
73
+ border-radius: var(--snice-border-radius-md, 4px);
74
+ transition: all var(--snice-transition-fast, 0.15s) ease;
75
+ }
76
+
77
+ .sidebar-toggle:hover {
78
+ background: var(--snice-color-background-secondary, #f8fafc);
79
+ color: var(--snice-color-text, #374151);
80
+ }
81
+
82
+ .main {
83
+ padding: var(--snice-spacing-lg, 2rem);
84
+ overflow-y: auto;
85
+ }
86
+
87
+ .footer {
88
+ padding: var(--snice-spacing-md, 1rem) var(--snice-spacing-lg, 2rem);
89
+ background: var(--snice-color-background, white);
90
+ border-top: 1px solid var(--snice-color-border, #d1d5db);
91
+ }
92
+
93
+ /* Mobile responsive */
94
+ @media (max-width: 768px) {
95
+ .layout {
96
+ grid-template-columns: 1fr;
97
+ }
98
+
99
+ :host([collapsed]) .layout {
100
+ grid-template-columns: 1fr;
101
+ }
102
+
103
+ .sidebar {
104
+ position: fixed;
105
+ top: 0;
106
+ left: 0;
107
+ width: 250px;
108
+ height: 100vh;
109
+ z-index: 1000;
110
+ transform: translateX(-100%);
111
+ transition: transform var(--snice-transition-normal, 0.25s) ease;
112
+ }
113
+
114
+ :host(:not([collapsed])) .sidebar {
115
+ transform: translateX(0);
116
+ }
117
+ }
@@ -0,0 +1,48 @@
1
+ import { element, property } from 'snice';
2
+ import css from './snice-layout-sidebar.css?inline';
3
+
4
+ @element('snice-layout-sidebar')
5
+ export class SniceLayoutSidebar extends HTMLElement {
6
+ @property({ type: Boolean, reflect: true })
7
+ collapsed = false;
8
+
9
+ html() {
10
+ return /*html*/`
11
+ <div class="layout">
12
+ <aside class="sidebar">
13
+ <div class="sidebar-header">
14
+ <slot name="brand">
15
+ <h2>App</h2>
16
+ </slot>
17
+ </div>
18
+ <nav class="sidebar-nav">
19
+ <slot name="nav"></slot>
20
+ </nav>
21
+ </aside>
22
+
23
+ <div class="content-area">
24
+ <header class="header">
25
+ <button class="sidebar-toggle" type="button" aria-label="Toggle sidebar">
26
+ <svg viewBox="0 0 24 24" width="20" height="20">
27
+ <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" fill="currentColor"/>
28
+ </svg>
29
+ </button>
30
+ <slot name="header"></slot>
31
+ </header>
32
+
33
+ <main class="main">
34
+ <slot></slot>
35
+ </main>
36
+
37
+ <footer class="footer">
38
+ <slot name="footer"></slot>
39
+ </footer>
40
+ </div>
41
+ </div>
42
+ `;
43
+ }
44
+
45
+ css() {
46
+ return css;
47
+ }
48
+ }
@@ -0,0 +1,103 @@
1
+ /* Split Layout - Two panels side by side */
2
+ :host {
3
+ display: block;
4
+ font-family: var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
5
+ }
6
+
7
+ .layout {
8
+ display: grid;
9
+ min-height: 100vh;
10
+ gap: 1px;
11
+ background: var(--snice-color-border, #d1d5db);
12
+ }
13
+
14
+ /* Horizontal split (default) */
15
+ :host([direction="horizontal"]) .layout,
16
+ :host(:not([direction])) .layout {
17
+ grid-template-columns: 1fr auto 1fr;
18
+ grid-template-rows: 1fr;
19
+ }
20
+
21
+ /* Vertical split */
22
+ :host([direction="vertical"]) .layout {
23
+ grid-template-rows: 1fr auto 1fr;
24
+ grid-template-columns: 1fr;
25
+ }
26
+
27
+ /* Ratio variants - Horizontal */
28
+ :host([ratio="50-50"]) .layout,
29
+ :host(:not([ratio])) .layout {
30
+ grid-template-columns: 1fr 1px 1fr;
31
+ }
32
+
33
+ :host([ratio="60-40"]) .layout {
34
+ grid-template-columns: 60fr 1px 40fr;
35
+ }
36
+
37
+ :host([ratio="70-30"]) .layout {
38
+ grid-template-columns: 70fr 1px 30fr;
39
+ }
40
+
41
+ :host([ratio="33-67"]) .layout {
42
+ grid-template-columns: 33fr 1px 67fr;
43
+ }
44
+
45
+ :host([ratio="67-33"]) .layout {
46
+ grid-template-columns: 67fr 1px 33fr;
47
+ }
48
+
49
+ /* Ratio variants - Vertical */
50
+ :host([direction="vertical"][ratio="50-50"]) .layout,
51
+ :host([direction="vertical"]:not([ratio])) .layout {
52
+ grid-template-rows: 1fr 1px 1fr;
53
+ }
54
+
55
+ :host([direction="vertical"][ratio="60-40"]) .layout {
56
+ grid-template-rows: 60fr 1px 40fr;
57
+ }
58
+
59
+ :host([direction="vertical"][ratio="70-30"]) .layout {
60
+ grid-template-rows: 70fr 1px 30fr;
61
+ }
62
+
63
+ :host([direction="vertical"][ratio="33-67"]) .layout {
64
+ grid-template-rows: 33fr 1px 67fr;
65
+ }
66
+
67
+ :host([direction="vertical"][ratio="67-33"]) .layout {
68
+ grid-template-rows: 67fr 1px 33fr;
69
+ }
70
+
71
+ .panel {
72
+ background: var(--snice-color-background, white);
73
+ overflow: auto;
74
+ }
75
+
76
+ .panel-left {
77
+ padding: var(--snice-spacing-lg, 2rem);
78
+ }
79
+
80
+ .panel-right {
81
+ padding: var(--snice-spacing-lg, 2rem);
82
+ }
83
+
84
+ .divider {
85
+ background: var(--snice-color-border, #d1d5db);
86
+ }
87
+
88
+ /* Mobile responsive */
89
+ @media (max-width: 768px) {
90
+ .layout {
91
+ grid-template-columns: 1fr !important;
92
+ grid-template-rows: auto auto auto !important;
93
+ }
94
+
95
+ .divider {
96
+ height: 1px;
97
+ }
98
+
99
+ .panel-left,
100
+ .panel-right {
101
+ padding: var(--snice-spacing-md, 1rem);
102
+ }
103
+ }
@@ -0,0 +1,29 @@
1
+ import { element, property } from 'snice';
2
+ import css from './snice-layout-split.css?inline';
3
+
4
+ @element('snice-layout-split')
5
+ export class SniceLayoutSplit extends HTMLElement {
6
+ @property({ reflect: true })
7
+ direction: 'horizontal' | 'vertical' = 'horizontal';
8
+
9
+ @property({ reflect: true })
10
+ ratio: '50-50' | '60-40' | '70-30' | '33-67' | '67-33' = '50-50';
11
+
12
+ html() {
13
+ return /*html*/`
14
+ <div class="layout">
15
+ <div class="panel panel-left">
16
+ <slot name="left"></slot>
17
+ </div>
18
+ <div class="divider"></div>
19
+ <div class="panel panel-right">
20
+ <slot name="right"></slot>
21
+ </div>
22
+ </div>
23
+ `;
24
+ }
25
+
26
+ css() {
27
+ return css;
28
+ }
29
+ }
@@ -0,0 +1,72 @@
1
+ /* Layout Component Styles */
2
+ :host {
3
+ display: block;
4
+ font-family: var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
5
+ }
6
+
7
+ .layout {
8
+ display: grid;
9
+ grid-template-rows: auto 1fr auto;
10
+ min-height: 100vh;
11
+ }
12
+
13
+ .header {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: space-between;
17
+ padding: var(--snice-spacing-md, 1rem) var(--snice-spacing-lg, 2rem);
18
+ background: var(--snice-color-background, white);
19
+ border-bottom: 1px solid var(--snice-color-border, #d1d5db);
20
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
21
+ }
22
+
23
+ .brand h1 {
24
+ margin: 0;
25
+ font-size: var(--snice-font-size-xl, 1.5rem);
26
+ color: var(--snice-color-text, #374151);
27
+ font-weight: var(--snice-font-weight-semibold, 600);
28
+ }
29
+
30
+ .nav {
31
+ display: flex;
32
+ gap: var(--snice-spacing-lg, 2rem);
33
+ }
34
+
35
+ .nav ::slotted(a) {
36
+ text-decoration: none;
37
+ color: var(--snice-color-text-secondary, #6b7280);
38
+ font-weight: var(--snice-font-weight-medium, 500);
39
+ padding: var(--snice-spacing-xs, 0.5rem) var(--snice-spacing-md, 1rem);
40
+ border-radius: var(--snice-border-radius-md, 4px);
41
+ transition: all var(--snice-transition-fast, 0.15s) ease;
42
+ }
43
+
44
+ .nav ::slotted(a:hover) {
45
+ color: var(--snice-color-primary, #3b82f6);
46
+ background: var(--snice-color-background-secondary, #f8fafc);
47
+ }
48
+
49
+ .nav ::slotted(a.active) {
50
+ color: var(--snice-color-primary, #3b82f6);
51
+ background: var(--snice-color-primary-light, #dbeafe);
52
+ font-weight: var(--snice-font-weight-semibold, 600);
53
+ }
54
+
55
+ .main {
56
+ padding: var(--snice-spacing-lg, 2rem);
57
+ background: var(--snice-color-background-secondary, #f8fafc);
58
+ overflow-y: auto;
59
+ }
60
+
61
+ .footer {
62
+ padding: var(--snice-spacing-md, 1rem) var(--snice-spacing-lg, 2rem);
63
+ background: var(--snice-color-background, white);
64
+ border-top: 1px solid var(--snice-color-border, #d1d5db);
65
+ text-align: center;
66
+ color: var(--snice-color-text-secondary, #6b7280);
67
+ font-size: var(--snice-font-size-sm, 0.875rem);
68
+ }
69
+
70
+ .footer p {
71
+ margin: 0;
72
+ }
@@ -0,0 +1,35 @@
1
+ import { element } from 'snice';
2
+ import css from './snice-layout.css?inline';
3
+
4
+ @element('snice-layout')
5
+ export class SniceLayout extends HTMLElement {
6
+ html() {
7
+ return /*html*/`
8
+ <div class="layout">
9
+ <header class="header">
10
+ <div class="brand">
11
+ <slot name="brand">
12
+ <h1>App</h1>
13
+ </slot>
14
+ </div>
15
+ <nav class="nav">
16
+ <slot name="nav"></slot>
17
+ </nav>
18
+ </header>
19
+
20
+ <main class="main">
21
+ <slot></slot>
22
+ </main>
23
+
24
+ <footer class="footer">
25
+ <slot name="footer">
26
+ </slot>
27
+ </footer>
28
+ </div>
29
+ `;
30
+ }
31
+
32
+ css() {
33
+ return css;
34
+ }
35
+ }
@@ -0,0 +1,5 @@
1
+ import type { SniceElement } from 'snice';
2
+
3
+ export interface SniceLayoutElement extends SniceElement {
4
+ // Add layout-specific properties and methods here as needed
5
+ }
@@ -0,0 +1,185 @@
1
+ import { controller, respond } from 'snice';
2
+ import type { LoginCredentials, LoginResult } from './snice-login.types';
3
+
4
+ interface AuthUser {
5
+ id: string;
6
+ username: string;
7
+ email: string;
8
+ role: string;
9
+ lastLogin?: string;
10
+ }
11
+
12
+ @controller('demo-auth-controller')
13
+ export class DemoAuthController {
14
+ element!: HTMLElement;
15
+
16
+ // Mock user database for demo purposes
17
+ private mockUsers: AuthUser[] = [
18
+ {
19
+ id: '1',
20
+ username: 'demo',
21
+ email: 'demo@example.com',
22
+ role: 'user'
23
+ },
24
+ {
25
+ id: '2',
26
+ username: 'admin',
27
+ email: 'admin@example.com',
28
+ role: 'admin'
29
+ },
30
+ {
31
+ id: '3',
32
+ username: 'test',
33
+ email: 'test@example.com',
34
+ role: 'user'
35
+ }
36
+ ];
37
+
38
+ // Valid passwords for demo (in real app, these would be hashed)
39
+ private mockPasswords: Record<string, string> = {
40
+ 'demo': 'password',
41
+ 'admin': 'admin123',
42
+ 'test': 'test123'
43
+ };
44
+
45
+ async attach(element: HTMLElement) {
46
+ this.element = element;
47
+ console.log('Demo Auth Controller attached to', element.tagName);
48
+ }
49
+
50
+ async detach(element: HTMLElement) {
51
+ console.log('Demo Auth Controller detached from', element.tagName);
52
+ }
53
+
54
+ @respond('login-user')
55
+ async handleLogin(credentials: LoginCredentials): Promise<LoginResult> {
56
+ console.log('Auth controller handling login request:', { username: credentials.username });
57
+
58
+ // Simulate network delay
59
+ await this.delay(200 + Math.random() * 200);
60
+
61
+ // Validate credentials
62
+ const result = await this.authenticateUser(credentials);
63
+
64
+ // Store successful login
65
+ if (result.success && result.user) {
66
+ this.storeUserSession(result.user, result.token!);
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ private async authenticateUser(credentials: LoginCredentials): Promise<LoginResult> {
73
+ const { username, password } = credentials;
74
+
75
+ // Basic validation
76
+ if (!username || !password) {
77
+ return {
78
+ success: false,
79
+ error: 'Username and password are required'
80
+ };
81
+ }
82
+
83
+ // Find user
84
+ const user = this.mockUsers.find(u => u.username.toLowerCase() === username.toLowerCase());
85
+
86
+ if (!user) {
87
+ return {
88
+ success: false,
89
+ error: 'Invalid username or password'
90
+ };
91
+ }
92
+
93
+ // Check password
94
+ const expectedPassword = this.mockPasswords[user.username];
95
+ if (password !== expectedPassword) {
96
+ return {
97
+ success: false,
98
+ error: 'Invalid username or password'
99
+ };
100
+ }
101
+
102
+ // Simulate random authentication failure (10% chance)
103
+ if (Math.random() < 0.1) {
104
+ return {
105
+ success: false,
106
+ error: 'Authentication service temporarily unavailable'
107
+ };
108
+ }
109
+
110
+ // Generate mock JWT token
111
+ const token = this.generateMockToken(user);
112
+
113
+ // Update last login
114
+ user.lastLogin = new Date().toISOString();
115
+
116
+ return {
117
+ success: true,
118
+ token,
119
+ user: {
120
+ id: user.id,
121
+ username: user.username,
122
+ email: user.email
123
+ }
124
+ };
125
+ }
126
+
127
+ private generateMockToken(user: AuthUser): string {
128
+ // This is just a demo token - in real apps use proper JWT libraries
129
+ const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
130
+ const payload = btoa(JSON.stringify({
131
+ sub: user.id,
132
+ username: user.username,
133
+ email: user.email,
134
+ role: user.role,
135
+ iat: Math.floor(Date.now() / 1000),
136
+ exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24) // 24 hours
137
+ }));
138
+ const signature = btoa('demo-signature-' + Math.random().toString(36));
139
+
140
+ return `${header}.${payload}.${signature}`;
141
+ }
142
+
143
+ private storeUserSession(user: AuthUser, token: string): void {
144
+ // In a real app, you might store this in localStorage, sessionStorage, or cookies
145
+ const session = {
146
+ user: {
147
+ id: user.id,
148
+ username: user.username,
149
+ email: user.email
150
+ },
151
+ token,
152
+ loginTime: new Date().toISOString()
153
+ };
154
+
155
+ // Store in sessionStorage for demo
156
+ sessionStorage.setItem('demoUserSession', JSON.stringify(session));
157
+
158
+ console.log('User session stored:', session);
159
+ }
160
+
161
+ private delay(ms: number): Promise<void> {
162
+ return new Promise(resolve => setTimeout(resolve, ms));
163
+ }
164
+
165
+ // Helper method to get current session (for other components to use)
166
+ getCurrentSession(): { user: AuthUser; token: string } | null {
167
+ try {
168
+ const sessionData = sessionStorage.getItem('demoUserSession');
169
+ return sessionData ? JSON.parse(sessionData) : null;
170
+ } catch {
171
+ return null;
172
+ }
173
+ }
174
+
175
+ // Helper method to logout
176
+ logout(): void {
177
+ sessionStorage.removeItem('demoUserSession');
178
+ console.log('User logged out');
179
+ }
180
+
181
+ // Helper method to check if user is authenticated
182
+ isAuthenticated(): boolean {
183
+ return this.getCurrentSession() !== null;
184
+ }
185
+ }