@svadmin/lite 0.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.
- package/README.md +149 -0
- package/package.json +38 -0
- package/src/components/LiteAlert.svelte +19 -0
- package/src/components/LiteForm.svelte +138 -0
- package/src/components/LiteLayout.svelte +58 -0
- package/src/components/LiteLogin.svelte +41 -0
- package/src/components/LitePagination.svelte +73 -0
- package/src/components/LiteSearch.svelte +41 -0
- package/src/components/LiteShow.svelte +96 -0
- package/src/components/LiteTable.svelte +132 -0
- package/src/index.ts +32 -0
- package/src/lite.css +460 -0
- package/src/schema-generator.ts +145 -0
- package/src/server-adapter.ts +291 -0
- package/static/enhance.js +56 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* LiteTable — Pure HTML table with <a> sort links.
|
|
4
|
+
* No JavaScript required — sorting and pagination are URL-driven.
|
|
5
|
+
*/
|
|
6
|
+
import type { ResourceDefinition, FieldDefinition } from '@svadmin/core';
|
|
7
|
+
import { t } from '@svadmin/core/i18n';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
records: Record<string, unknown>[];
|
|
11
|
+
resource: ResourceDefinition;
|
|
12
|
+
currentSort?: string;
|
|
13
|
+
currentOrder?: 'asc' | 'desc';
|
|
14
|
+
currentSearch?: string;
|
|
15
|
+
basePath?: string;
|
|
16
|
+
/** Show edit/delete action buttons */
|
|
17
|
+
canEdit?: boolean;
|
|
18
|
+
canDelete?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
records,
|
|
23
|
+
resource,
|
|
24
|
+
currentSort,
|
|
25
|
+
currentOrder = 'asc',
|
|
26
|
+
currentSearch,
|
|
27
|
+
basePath = '/lite',
|
|
28
|
+
canEdit = true,
|
|
29
|
+
canDelete = true,
|
|
30
|
+
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
const pk = resource.primaryKey ?? 'id';
|
|
33
|
+
const listFields = $derived(
|
|
34
|
+
resource.fields.filter(f => f.showInList !== false)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function sortUrl(field: FieldDefinition): string {
|
|
38
|
+
const newOrder = currentSort === field.key && currentOrder === 'asc' ? 'desc' : 'asc';
|
|
39
|
+
const params = new URLSearchParams({ sort: field.key, order: newOrder });
|
|
40
|
+
if (currentSearch) params.set('q', currentSearch);
|
|
41
|
+
return `?${params.toString()}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sortIndicator(field: FieldDefinition): string {
|
|
45
|
+
if (currentSort !== field.key) return '⇅';
|
|
46
|
+
return currentOrder === 'asc' ? '↑' : '↓';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatValue(value: unknown, field: FieldDefinition): string {
|
|
50
|
+
if (value == null) return '—';
|
|
51
|
+
if (field.type === 'boolean') return ''; // handled in template
|
|
52
|
+
if (field.type === 'date') {
|
|
53
|
+
try { return new Date(value as string).toLocaleDateString(); } catch { return String(value); }
|
|
54
|
+
}
|
|
55
|
+
if (field.type === 'select' && field.options) {
|
|
56
|
+
const opt = field.options.find(o => o.value === value);
|
|
57
|
+
return opt?.label ?? String(value);
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(value)) return value.join(', ');
|
|
60
|
+
return String(value);
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<table class="lite-table">
|
|
65
|
+
<thead>
|
|
66
|
+
<tr>
|
|
67
|
+
{#each listFields as field}
|
|
68
|
+
<th>
|
|
69
|
+
{#if field.sortable !== false}
|
|
70
|
+
<a href={sortUrl(field)}>
|
|
71
|
+
{field.label}
|
|
72
|
+
<span class="sort-indicator">{sortIndicator(field)}</span>
|
|
73
|
+
</a>
|
|
74
|
+
{:else}
|
|
75
|
+
{field.label}
|
|
76
|
+
{/if}
|
|
77
|
+
</th>
|
|
78
|
+
{/each}
|
|
79
|
+
{#if canEdit || canDelete}
|
|
80
|
+
<th style="text-align:right;">{t('common.actions') || 'Actions'}</th>
|
|
81
|
+
{/if}
|
|
82
|
+
</tr>
|
|
83
|
+
</thead>
|
|
84
|
+
<tbody>
|
|
85
|
+
{#each records as record}
|
|
86
|
+
{@const id = record[pk]}
|
|
87
|
+
<tr>
|
|
88
|
+
{#each listFields as field}
|
|
89
|
+
<td>
|
|
90
|
+
{#if field.type === 'boolean'}
|
|
91
|
+
<span class="lite-bool {record[field.key] ? 'lite-bool-true' : ''}"></span>
|
|
92
|
+
{:else if field.type === 'tags' && Array.isArray(record[field.key])}
|
|
93
|
+
{#each (record[field.key] as string[]).slice(0, 3) as tag}
|
|
94
|
+
<span class="lite-badge">{tag}</span>
|
|
95
|
+
{/each}
|
|
96
|
+
{:else if field.type === 'select' && field.options}
|
|
97
|
+
<span class="lite-badge">{formatValue(record[field.key], field)}</span>
|
|
98
|
+
{:else}
|
|
99
|
+
{formatValue(record[field.key], field)}
|
|
100
|
+
{/if}
|
|
101
|
+
</td>
|
|
102
|
+
{/each}
|
|
103
|
+
{#if canEdit || canDelete}
|
|
104
|
+
<td class="actions">
|
|
105
|
+
{#if canEdit}
|
|
106
|
+
<a href="{basePath}/{resource.name}/edit/{id}" class="lite-btn lite-btn-sm">{t('common.edit') || 'Edit'}</a>
|
|
107
|
+
{/if}
|
|
108
|
+
{#if canDelete}
|
|
109
|
+
<!-- Delete uses <details> for no-JS confirmation -->
|
|
110
|
+
<details class="lite-confirm-details">
|
|
111
|
+
<summary class="lite-btn lite-btn-sm lite-btn-danger">{t('common.delete') || 'Delete'}</summary>
|
|
112
|
+
<div class="lite-confirm-panel">
|
|
113
|
+
<p style="margin:0 0 8px;font-size:13px;">{t('common.areYouSure') || 'Are you sure?'}</p>
|
|
114
|
+
<form method="POST" action="?/delete" style="display:inline;">
|
|
115
|
+
<input type="hidden" name="id" value={String(id)} />
|
|
116
|
+
<button type="submit" class="lite-btn lite-btn-sm lite-btn-danger">{t('common.confirm') || 'Confirm'}</button>
|
|
117
|
+
</form>
|
|
118
|
+
</div>
|
|
119
|
+
</details>
|
|
120
|
+
{/if}
|
|
121
|
+
</td>
|
|
122
|
+
{/if}
|
|
123
|
+
</tr>
|
|
124
|
+
{:else}
|
|
125
|
+
<tr>
|
|
126
|
+
<td colspan={listFields.length + (canEdit || canDelete ? 1 : 0)} style="text-align:center;padding:40px;color:#9ca3af;">
|
|
127
|
+
{t('common.noData') || 'No records found.'}
|
|
128
|
+
</td>
|
|
129
|
+
</tr>
|
|
130
|
+
{/each}
|
|
131
|
+
</tbody>
|
|
132
|
+
</table>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @svadmin/lite — Lightweight SSR Admin UI
|
|
2
|
+
// Zero client-side JS required. IE11 compatible.
|
|
3
|
+
|
|
4
|
+
// Server utilities (use in +page.server.ts / hooks.server.ts)
|
|
5
|
+
export {
|
|
6
|
+
createListLoader,
|
|
7
|
+
createDetailLoader,
|
|
8
|
+
createCrudActions,
|
|
9
|
+
createAuthGuard,
|
|
10
|
+
createAuthActions,
|
|
11
|
+
createLegacyRedirectHook,
|
|
12
|
+
isLegacyBrowser,
|
|
13
|
+
} from './server-adapter';
|
|
14
|
+
export type { ListLoaderResult } from './server-adapter';
|
|
15
|
+
|
|
16
|
+
// Schema generator (use with sveltekit-superforms)
|
|
17
|
+
export {
|
|
18
|
+
fieldsToZodSchema,
|
|
19
|
+
resourceToZodSchema,
|
|
20
|
+
fieldToInputType,
|
|
21
|
+
fieldToPlaceholder,
|
|
22
|
+
} from './schema-generator';
|
|
23
|
+
|
|
24
|
+
// UI Components (use in +page.svelte with csr = false)
|
|
25
|
+
export { default as LiteLayout } from './components/LiteLayout.svelte';
|
|
26
|
+
export { default as LiteTable } from './components/LiteTable.svelte';
|
|
27
|
+
export { default as LitePagination } from './components/LitePagination.svelte';
|
|
28
|
+
export { default as LiteForm } from './components/LiteForm.svelte';
|
|
29
|
+
export { default as LiteLogin } from './components/LiteLogin.svelte';
|
|
30
|
+
export { default as LiteShow } from './components/LiteShow.svelte';
|
|
31
|
+
export { default as LiteSearch } from './components/LiteSearch.svelte';
|
|
32
|
+
export { default as LiteAlert } from './components/LiteAlert.svelte';
|
package/src/lite.css
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @svadmin/lite — IE11 Compatible CSS
|
|
3
|
+
*
|
|
4
|
+
* RULES:
|
|
5
|
+
* - No CSS variables (var(--x))
|
|
6
|
+
* - No color-mix(), :is(), :where()
|
|
7
|
+
* - No CSS Grid (use flexbox/float only)
|
|
8
|
+
* - No gap property on flexbox (use margins)
|
|
9
|
+
* - No backdrop-filter
|
|
10
|
+
* - All colors are hardcoded
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/* ─── Reset & Base ─────────────────────────────────────────── */
|
|
14
|
+
*, *::before, *::after {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
html, body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 0;
|
|
21
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
22
|
+
font-size: 14px;
|
|
23
|
+
line-height: 1.5;
|
|
24
|
+
color: #1a1a1a;
|
|
25
|
+
background: #f5f5f5;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
a {
|
|
29
|
+
color: #2563eb;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
}
|
|
32
|
+
a:hover {
|
|
33
|
+
text-decoration: underline;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ─── Layout ───────────────────────────────────────────────── */
|
|
37
|
+
.lite-wrapper {
|
|
38
|
+
display: -webkit-box;
|
|
39
|
+
display: -ms-flexbox;
|
|
40
|
+
display: flex;
|
|
41
|
+
min-height: 100vh;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.lite-sidebar {
|
|
45
|
+
width: 220px;
|
|
46
|
+
background: #1e293b;
|
|
47
|
+
color: #e2e8f0;
|
|
48
|
+
padding: 16px 0;
|
|
49
|
+
-ms-flex-negative: 0;
|
|
50
|
+
flex-shrink: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.lite-sidebar-brand {
|
|
54
|
+
padding: 0 16px 16px;
|
|
55
|
+
font-size: 18px;
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
color: #fff;
|
|
58
|
+
border-bottom: 1px solid #334155;
|
|
59
|
+
margin-bottom: 8px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.lite-sidebar a {
|
|
63
|
+
display: block;
|
|
64
|
+
padding: 8px 16px;
|
|
65
|
+
color: #94a3b8;
|
|
66
|
+
font-size: 13px;
|
|
67
|
+
}
|
|
68
|
+
.lite-sidebar a:hover,
|
|
69
|
+
.lite-sidebar a.active {
|
|
70
|
+
background: #334155;
|
|
71
|
+
color: #fff;
|
|
72
|
+
text-decoration: none;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.lite-main {
|
|
76
|
+
-webkit-box-flex: 1;
|
|
77
|
+
-ms-flex: 1;
|
|
78
|
+
flex: 1;
|
|
79
|
+
padding: 24px;
|
|
80
|
+
min-width: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.lite-header {
|
|
84
|
+
display: -webkit-box;
|
|
85
|
+
display: -ms-flexbox;
|
|
86
|
+
display: flex;
|
|
87
|
+
-webkit-box-align: center;
|
|
88
|
+
-ms-flex-align: center;
|
|
89
|
+
align-items: center;
|
|
90
|
+
-webkit-box-pack: justify;
|
|
91
|
+
-ms-flex-pack: justify;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
margin-bottom: 20px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.lite-header h1 {
|
|
97
|
+
font-size: 22px;
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
margin: 0;
|
|
100
|
+
color: #0f172a;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* ─── Cards ────────────────────────────────────────────────── */
|
|
104
|
+
.lite-card {
|
|
105
|
+
background: #fff;
|
|
106
|
+
border: 1px solid #e2e8f0;
|
|
107
|
+
border-radius: 6px;
|
|
108
|
+
padding: 20px;
|
|
109
|
+
margin-bottom: 16px;
|
|
110
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* ─── Table ────────────────────────────────────────────────── */
|
|
114
|
+
.lite-table {
|
|
115
|
+
width: 100%;
|
|
116
|
+
border-collapse: collapse;
|
|
117
|
+
background: #fff;
|
|
118
|
+
border: 1px solid #e2e8f0;
|
|
119
|
+
border-radius: 6px;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.lite-table th {
|
|
123
|
+
text-align: left;
|
|
124
|
+
padding: 10px 12px;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
text-transform: uppercase;
|
|
128
|
+
letter-spacing: 0.05em;
|
|
129
|
+
color: #64748b;
|
|
130
|
+
background: #f8fafc;
|
|
131
|
+
border-bottom: 2px solid #e2e8f0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.lite-table th a {
|
|
135
|
+
color: #64748b;
|
|
136
|
+
}
|
|
137
|
+
.lite-table th a:hover {
|
|
138
|
+
color: #0f172a;
|
|
139
|
+
}
|
|
140
|
+
.lite-table th .sort-indicator {
|
|
141
|
+
font-size: 10px;
|
|
142
|
+
margin-left: 4px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.lite-table td {
|
|
146
|
+
padding: 10px 12px;
|
|
147
|
+
border-bottom: 1px solid #f1f5f9;
|
|
148
|
+
color: #334155;
|
|
149
|
+
font-size: 13px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.lite-table tr:hover td {
|
|
153
|
+
background: #f8fafc;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.lite-table td.actions {
|
|
157
|
+
text-align: right;
|
|
158
|
+
white-space: nowrap;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* ─── Buttons ──────────────────────────────────────────────── */
|
|
162
|
+
.lite-btn {
|
|
163
|
+
display: inline-block;
|
|
164
|
+
padding: 6px 14px;
|
|
165
|
+
font-size: 13px;
|
|
166
|
+
font-weight: 500;
|
|
167
|
+
border: 1px solid #d1d5db;
|
|
168
|
+
border-radius: 4px;
|
|
169
|
+
background: #fff;
|
|
170
|
+
color: #374151;
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
text-decoration: none;
|
|
173
|
+
vertical-align: middle;
|
|
174
|
+
line-height: 1.5;
|
|
175
|
+
}
|
|
176
|
+
.lite-btn:hover {
|
|
177
|
+
background: #f9fafb;
|
|
178
|
+
text-decoration: none;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.lite-btn-primary {
|
|
182
|
+
background: #2563eb;
|
|
183
|
+
border-color: #2563eb;
|
|
184
|
+
color: #fff;
|
|
185
|
+
}
|
|
186
|
+
.lite-btn-primary:hover {
|
|
187
|
+
background: #1d4ed8;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.lite-btn-danger {
|
|
191
|
+
background: #fff;
|
|
192
|
+
border-color: #fca5a5;
|
|
193
|
+
color: #dc2626;
|
|
194
|
+
}
|
|
195
|
+
.lite-btn-danger:hover {
|
|
196
|
+
background: #fef2f2;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.lite-btn-sm {
|
|
200
|
+
padding: 3px 8px;
|
|
201
|
+
font-size: 12px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* ─── Forms ────────────────────────────────────────────────── */
|
|
205
|
+
.lite-form-group {
|
|
206
|
+
margin-bottom: 16px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.lite-form-group label {
|
|
210
|
+
display: block;
|
|
211
|
+
font-size: 13px;
|
|
212
|
+
font-weight: 500;
|
|
213
|
+
color: #374151;
|
|
214
|
+
margin-bottom: 4px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.lite-form-group label .required {
|
|
218
|
+
color: #dc2626;
|
|
219
|
+
margin-left: 2px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.lite-input {
|
|
223
|
+
display: block;
|
|
224
|
+
width: 100%;
|
|
225
|
+
padding: 8px 10px;
|
|
226
|
+
font-size: 14px;
|
|
227
|
+
border: 1px solid #d1d5db;
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
background: #fff;
|
|
230
|
+
color: #1f2937;
|
|
231
|
+
}
|
|
232
|
+
.lite-input:focus {
|
|
233
|
+
outline: none;
|
|
234
|
+
border-color: #2563eb;
|
|
235
|
+
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.15);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.lite-input-error {
|
|
239
|
+
border-color: #dc2626;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.lite-error-text {
|
|
243
|
+
color: #dc2626;
|
|
244
|
+
font-size: 12px;
|
|
245
|
+
margin-top: 4px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.lite-select {
|
|
249
|
+
display: block;
|
|
250
|
+
width: 100%;
|
|
251
|
+
padding: 8px 10px;
|
|
252
|
+
font-size: 14px;
|
|
253
|
+
border: 1px solid #d1d5db;
|
|
254
|
+
border-radius: 4px;
|
|
255
|
+
background: #fff;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
textarea.lite-input {
|
|
259
|
+
min-height: 100px;
|
|
260
|
+
resize: vertical;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Checkbox */
|
|
264
|
+
.lite-checkbox-group {
|
|
265
|
+
display: -webkit-box;
|
|
266
|
+
display: -ms-flexbox;
|
|
267
|
+
display: flex;
|
|
268
|
+
-webkit-box-align: center;
|
|
269
|
+
-ms-flex-align: center;
|
|
270
|
+
align-items: center;
|
|
271
|
+
}
|
|
272
|
+
.lite-checkbox-group input {
|
|
273
|
+
margin-right: 8px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ─── Pagination ───────────────────────────────────────────── */
|
|
277
|
+
.lite-pagination {
|
|
278
|
+
display: -webkit-box;
|
|
279
|
+
display: -ms-flexbox;
|
|
280
|
+
display: flex;
|
|
281
|
+
-webkit-box-align: center;
|
|
282
|
+
-ms-flex-align: center;
|
|
283
|
+
align-items: center;
|
|
284
|
+
margin-top: 16px;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.lite-pagination a,
|
|
288
|
+
.lite-pagination span {
|
|
289
|
+
display: inline-block;
|
|
290
|
+
padding: 6px 12px;
|
|
291
|
+
margin-right: 4px;
|
|
292
|
+
border: 1px solid #d1d5db;
|
|
293
|
+
border-radius: 4px;
|
|
294
|
+
font-size: 13px;
|
|
295
|
+
color: #374151;
|
|
296
|
+
}
|
|
297
|
+
.lite-pagination a:hover {
|
|
298
|
+
background: #f3f4f6;
|
|
299
|
+
text-decoration: none;
|
|
300
|
+
}
|
|
301
|
+
.lite-pagination span.active {
|
|
302
|
+
background: #2563eb;
|
|
303
|
+
border-color: #2563eb;
|
|
304
|
+
color: #fff;
|
|
305
|
+
}
|
|
306
|
+
.lite-pagination span.disabled {
|
|
307
|
+
color: #9ca3af;
|
|
308
|
+
cursor: default;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.lite-pagination-info {
|
|
312
|
+
margin-left: auto;
|
|
313
|
+
font-size: 13px;
|
|
314
|
+
color: #6b7280;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* ─── Search Bar ───────────────────────────────────────────── */
|
|
318
|
+
.lite-search {
|
|
319
|
+
display: -webkit-box;
|
|
320
|
+
display: -ms-flexbox;
|
|
321
|
+
display: flex;
|
|
322
|
+
margin-bottom: 16px;
|
|
323
|
+
}
|
|
324
|
+
.lite-search input {
|
|
325
|
+
-webkit-box-flex: 1;
|
|
326
|
+
-ms-flex: 1;
|
|
327
|
+
flex: 1;
|
|
328
|
+
margin-right: 8px;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* ─── Alerts / Messages ────────────────────────────────────── */
|
|
332
|
+
.lite-alert {
|
|
333
|
+
padding: 12px 16px;
|
|
334
|
+
border-radius: 4px;
|
|
335
|
+
margin-bottom: 16px;
|
|
336
|
+
font-size: 13px;
|
|
337
|
+
}
|
|
338
|
+
.lite-alert-success {
|
|
339
|
+
background: #f0fdf4;
|
|
340
|
+
border: 1px solid #bbf7d0;
|
|
341
|
+
color: #166534;
|
|
342
|
+
}
|
|
343
|
+
.lite-alert-error {
|
|
344
|
+
background: #fef2f2;
|
|
345
|
+
border: 1px solid #fecaca;
|
|
346
|
+
color: #991b1b;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* ─── Delete Confirmation ──────────────────────────────────── */
|
|
350
|
+
.lite-confirm {
|
|
351
|
+
display: inline;
|
|
352
|
+
}
|
|
353
|
+
.lite-confirm-details {
|
|
354
|
+
position: relative;
|
|
355
|
+
display: inline-block;
|
|
356
|
+
}
|
|
357
|
+
.lite-confirm-details summary {
|
|
358
|
+
cursor: pointer;
|
|
359
|
+
list-style: none;
|
|
360
|
+
}
|
|
361
|
+
.lite-confirm-details summary::-webkit-details-marker {
|
|
362
|
+
display: none;
|
|
363
|
+
}
|
|
364
|
+
.lite-confirm-panel {
|
|
365
|
+
position: absolute;
|
|
366
|
+
right: 0;
|
|
367
|
+
top: 100%;
|
|
368
|
+
z-index: 10;
|
|
369
|
+
background: #fff;
|
|
370
|
+
border: 1px solid #e5e7eb;
|
|
371
|
+
border-radius: 6px;
|
|
372
|
+
padding: 12px;
|
|
373
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
374
|
+
white-space: nowrap;
|
|
375
|
+
margin-top: 4px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* ─── Login Page ───────────────────────────────────────────── */
|
|
379
|
+
.lite-login-wrapper {
|
|
380
|
+
display: -webkit-box;
|
|
381
|
+
display: -ms-flexbox;
|
|
382
|
+
display: flex;
|
|
383
|
+
-webkit-box-align: center;
|
|
384
|
+
-ms-flex-align: center;
|
|
385
|
+
align-items: center;
|
|
386
|
+
-webkit-box-pack: center;
|
|
387
|
+
-ms-flex-pack: center;
|
|
388
|
+
justify-content: center;
|
|
389
|
+
min-height: 100vh;
|
|
390
|
+
background: #f1f5f9;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.lite-login-card {
|
|
394
|
+
width: 380px;
|
|
395
|
+
max-width: 90%;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* ─── Badge / Tag ──────────────────────────────────────────── */
|
|
399
|
+
.lite-badge {
|
|
400
|
+
display: inline-block;
|
|
401
|
+
padding: 2px 8px;
|
|
402
|
+
font-size: 11px;
|
|
403
|
+
font-weight: 500;
|
|
404
|
+
border-radius: 12px;
|
|
405
|
+
background: #f1f5f9;
|
|
406
|
+
color: #475569;
|
|
407
|
+
border: 1px solid #e2e8f0;
|
|
408
|
+
margin-right: 4px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.lite-badge-success {
|
|
412
|
+
background: #f0fdf4;
|
|
413
|
+
color: #166534;
|
|
414
|
+
border-color: #bbf7d0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* ─── Boolean indicator ────────────────────────────────────── */
|
|
418
|
+
.lite-bool {
|
|
419
|
+
display: inline-block;
|
|
420
|
+
width: 10px;
|
|
421
|
+
height: 10px;
|
|
422
|
+
border-radius: 50%;
|
|
423
|
+
background: #d1d5db;
|
|
424
|
+
}
|
|
425
|
+
.lite-bool-true {
|
|
426
|
+
background: #22c55e;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* ─── Responsive ───────────────────────────────────────────── */
|
|
430
|
+
@media (max-width: 768px) {
|
|
431
|
+
.lite-sidebar {
|
|
432
|
+
display: none;
|
|
433
|
+
}
|
|
434
|
+
.lite-main {
|
|
435
|
+
padding: 12px;
|
|
436
|
+
}
|
|
437
|
+
.lite-table {
|
|
438
|
+
font-size: 12px;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* ─── Print ────────────────────────────────────────────────── */
|
|
443
|
+
@media print {
|
|
444
|
+
.lite-sidebar,
|
|
445
|
+
.lite-search,
|
|
446
|
+
.lite-pagination,
|
|
447
|
+
.lite-btn,
|
|
448
|
+
.lite-confirm {
|
|
449
|
+
display: none !important;
|
|
450
|
+
}
|
|
451
|
+
.lite-main {
|
|
452
|
+
padding: 0;
|
|
453
|
+
}
|
|
454
|
+
.lite-table {
|
|
455
|
+
border: 1px solid #000;
|
|
456
|
+
}
|
|
457
|
+
.lite-table th {
|
|
458
|
+
background: #eee !important;
|
|
459
|
+
}
|
|
460
|
+
}
|