@svadmin/lite 0.6.0 → 0.7.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 +85 -9
- package/dist/components/LiteDynamicFormList.svelte +169 -0
- package/dist/components/LiteDynamicFormList.svelte.d.ts +45 -0
- package/dist/components/LiteFilterBuilder.svelte +205 -0
- package/dist/components/LiteFilterBuilder.svelte.d.ts +19 -0
- package/dist/components/LiteShowField.svelte +22 -0
- package/dist/components/LiteTransfer.svelte +185 -0
- package/dist/components/LiteTransfer.svelte.d.ts +18 -0
- package/dist/components/fields/LiteAvatarField.svelte +127 -0
- package/dist/components/fields/LiteAvatarField.svelte.d.ts +21 -0
- package/dist/components/fields/LiteCascader.svelte +119 -0
- package/dist/components/fields/LiteCascader.svelte.d.ts +24 -0
- package/dist/components/fields/LiteCodeField.svelte +71 -0
- package/dist/components/fields/LiteCodeField.svelte.d.ts +16 -0
- package/dist/components/fields/LiteCopyField.svelte +74 -0
- package/dist/components/fields/LiteCopyField.svelte.d.ts +18 -0
- package/dist/components/fields/LiteCurrencyField.svelte +126 -0
- package/dist/components/fields/LiteCurrencyField.svelte.d.ts +21 -0
- package/dist/components/fields/LiteDateRangeField.svelte +147 -0
- package/dist/components/fields/LiteDateRangeField.svelte.d.ts +26 -0
- package/dist/components/fields/LitePercentField.svelte +124 -0
- package/dist/components/fields/LitePercentField.svelte.d.ts +17 -0
- package/dist/components/fields/LitePhoneField.svelte +81 -0
- package/dist/components/fields/LitePhoneField.svelte.d.ts +17 -0
- package/dist/components/fields/LiteRatingField.svelte +105 -0
- package/dist/components/fields/LiteRatingField.svelte.d.ts +16 -0
- package/dist/components/fields/LiteTreeSelect.svelte +143 -0
- package/dist/components/fields/LiteTreeSelect.svelte.d.ts +23 -0
- package/dist/components/fields/index.d.ts +12 -0
- package/dist/components/fields/index.js +10 -0
- package/dist/components/pages/LiteListPage.svelte +14 -12
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/lite.css +205 -0
- package/dist/schema-generator.js +15 -4
- package/package.json +4 -3
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { t } from '@svadmin/core/i18n';
|
|
3
|
+
|
|
4
|
+
export interface TransferItem {
|
|
5
|
+
key: string | number;
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
dataSource?: TransferItem[];
|
|
13
|
+
targetKeys?: (string | number)[];
|
|
14
|
+
titles?: [string, string];
|
|
15
|
+
name?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
label?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
dataSource = [],
|
|
23
|
+
targetKeys = [],
|
|
24
|
+
titles = ['Available', 'Selected'],
|
|
25
|
+
name = 'targetKeys',
|
|
26
|
+
disabled = false,
|
|
27
|
+
label,
|
|
28
|
+
description,
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const targetKeySet = $derived(new Set(targetKeys.map(String)));
|
|
32
|
+
const sourceItems = $derived(dataSource.filter((item) => !targetKeySet.has(String(item.key))));
|
|
33
|
+
const targetItems = $derived(dataSource.filter((item) => targetKeySet.has(String(item.key))));
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div class="lite-transfer-container lite-form-group">
|
|
37
|
+
{#if label}
|
|
38
|
+
<span class="lite-label">{label}</span>
|
|
39
|
+
{/if}
|
|
40
|
+
{#if description}
|
|
41
|
+
<p class="lite-help-text">{description}</p>
|
|
42
|
+
{/if}
|
|
43
|
+
|
|
44
|
+
<div class="lite-transfer-boxes">
|
|
45
|
+
<!-- Source Box -->
|
|
46
|
+
<div class="lite-transfer-panel">
|
|
47
|
+
<div class="lite-transfer-header">
|
|
48
|
+
<strong>{titles[0]}</strong>
|
|
49
|
+
<span class="lite-badge">{sourceItems.length}</span>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="lite-transfer-body">
|
|
52
|
+
{#if sourceItems.length === 0}
|
|
53
|
+
<div class="lite-transfer-empty">{t('common.empty') || 'No items'}</div>
|
|
54
|
+
{:else}
|
|
55
|
+
{#each sourceItems as item (item.key)}
|
|
56
|
+
<label class="lite-transfer-item">
|
|
57
|
+
<input
|
|
58
|
+
type="checkbox"
|
|
59
|
+
name="_transfer_source_keys"
|
|
60
|
+
value={String(item.key)}
|
|
61
|
+
disabled={disabled || item.disabled}
|
|
62
|
+
/>
|
|
63
|
+
<span class="lite-transfer-item-title">{item.title}</span>
|
|
64
|
+
</label>
|
|
65
|
+
{/each}
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<!-- Move Actions -->
|
|
71
|
+
<div class="lite-transfer-operations">
|
|
72
|
+
<button
|
|
73
|
+
type="submit"
|
|
74
|
+
name="_action"
|
|
75
|
+
value="transfer_to_target"
|
|
76
|
+
class="lite-btn lite-btn-sm lite-btn-primary"
|
|
77
|
+
{disabled}
|
|
78
|
+
title="Move selected right"
|
|
79
|
+
>
|
|
80
|
+
→
|
|
81
|
+
</button>
|
|
82
|
+
<button
|
|
83
|
+
type="submit"
|
|
84
|
+
name="_action"
|
|
85
|
+
value="transfer_to_source"
|
|
86
|
+
class="lite-btn lite-btn-sm"
|
|
87
|
+
{disabled}
|
|
88
|
+
title="Move selected left"
|
|
89
|
+
>
|
|
90
|
+
←
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<!-- Target Box -->
|
|
95
|
+
<div class="lite-transfer-panel">
|
|
96
|
+
<div class="lite-transfer-header">
|
|
97
|
+
<strong>{titles[1]}</strong>
|
|
98
|
+
<span class="lite-badge lite-badge-indigo">{targetItems.length}</span>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="lite-transfer-body">
|
|
101
|
+
{#if targetItems.length === 0}
|
|
102
|
+
<div class="lite-transfer-empty">{t('common.empty') || 'No items selected'}</div>
|
|
103
|
+
{:else}
|
|
104
|
+
{#each targetItems as item (item.key)}
|
|
105
|
+
<label class="lite-transfer-item">
|
|
106
|
+
<input
|
|
107
|
+
type="checkbox"
|
|
108
|
+
name="_transfer_target_keys"
|
|
109
|
+
value={String(item.key)}
|
|
110
|
+
disabled={disabled || item.disabled}
|
|
111
|
+
/>
|
|
112
|
+
<input type="hidden" {name} value={String(item.key)} />
|
|
113
|
+
<span class="lite-transfer-item-title">{item.title}</span>
|
|
114
|
+
</label>
|
|
115
|
+
{/each}
|
|
116
|
+
{/if}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<style>
|
|
123
|
+
.lite-transfer-container {
|
|
124
|
+
margin-bottom: 16px;
|
|
125
|
+
}
|
|
126
|
+
.lite-transfer-boxes {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
}
|
|
130
|
+
.lite-transfer-panel {
|
|
131
|
+
flex: 1;
|
|
132
|
+
border: 1px solid #cbd5e1;
|
|
133
|
+
border-radius: 6px;
|
|
134
|
+
background: #ffffff;
|
|
135
|
+
min-height: 180px;
|
|
136
|
+
max-height: 280px;
|
|
137
|
+
display: flex;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
}
|
|
140
|
+
.lite-transfer-header {
|
|
141
|
+
display: flex;
|
|
142
|
+
justify-content: space-between;
|
|
143
|
+
align-items: center;
|
|
144
|
+
padding: 8px 12px;
|
|
145
|
+
background: #f8fafc;
|
|
146
|
+
border-bottom: 1px solid #e2e8f0;
|
|
147
|
+
font-size: 13px;
|
|
148
|
+
}
|
|
149
|
+
.lite-transfer-body {
|
|
150
|
+
flex: 1;
|
|
151
|
+
overflow-y: auto;
|
|
152
|
+
padding: 6px;
|
|
153
|
+
}
|
|
154
|
+
.lite-transfer-item {
|
|
155
|
+
display: flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
padding: 6px 8px;
|
|
158
|
+
border-radius: 4px;
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
font-size: 13px;
|
|
161
|
+
}
|
|
162
|
+
.lite-transfer-item:hover {
|
|
163
|
+
background: #f1f5f9;
|
|
164
|
+
}
|
|
165
|
+
.lite-transfer-item input {
|
|
166
|
+
margin-right: 8px;
|
|
167
|
+
}
|
|
168
|
+
.lite-transfer-operations {
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
margin: 0 12px;
|
|
172
|
+
}
|
|
173
|
+
.lite-transfer-operations .lite-btn:first-child {
|
|
174
|
+
margin-bottom: 6px;
|
|
175
|
+
}
|
|
176
|
+
.lite-transfer-boxes > .lite-transfer-panel:first-child {
|
|
177
|
+
margin-right: 0;
|
|
178
|
+
}
|
|
179
|
+
.lite-transfer-empty {
|
|
180
|
+
text-align: center;
|
|
181
|
+
padding: 30px 10px;
|
|
182
|
+
color: #94a3b8;
|
|
183
|
+
font-size: 12px;
|
|
184
|
+
}
|
|
185
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface TransferItem {
|
|
2
|
+
key: string | number;
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface Props {
|
|
8
|
+
dataSource?: TransferItem[];
|
|
9
|
+
targetKeys?: (string | number)[];
|
|
10
|
+
titles?: [string, string];
|
|
11
|
+
name?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
label?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const LiteTransfer: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type LiteTransfer = ReturnType<typeof LiteTransfer>;
|
|
18
|
+
export default LiteTransfer;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export type LiteAvatarStatus = 'online' | 'offline' | 'busy' | 'away' | 'success' | 'warning' | 'error' | 'neutral';
|
|
3
|
+
export type LiteAvatarSize = 'xs' | 'sm' | 'default' | 'lg';
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
field?: FieldDefinition;
|
|
11
|
+
value?: unknown;
|
|
12
|
+
error?: string[];
|
|
13
|
+
mode?: 'show' | 'edit' | 'create';
|
|
14
|
+
src?: string | null;
|
|
15
|
+
name?: string | null;
|
|
16
|
+
subtitle?: string | null;
|
|
17
|
+
status?: LiteAvatarStatus;
|
|
18
|
+
size?: LiteAvatarSize;
|
|
19
|
+
shape?: 'circle' | 'square';
|
|
20
|
+
showName?: boolean;
|
|
21
|
+
nullLabel?: string;
|
|
22
|
+
class?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
field,
|
|
27
|
+
value,
|
|
28
|
+
error = [],
|
|
29
|
+
mode = 'show',
|
|
30
|
+
src,
|
|
31
|
+
name,
|
|
32
|
+
subtitle,
|
|
33
|
+
status,
|
|
34
|
+
size = 'sm',
|
|
35
|
+
shape = 'circle',
|
|
36
|
+
showName = false,
|
|
37
|
+
nullLabel = '—',
|
|
38
|
+
class: className = '',
|
|
39
|
+
}: Props = $props();
|
|
40
|
+
|
|
41
|
+
const resolvedSrc = $derived.by(() => {
|
|
42
|
+
if (src !== undefined) return src;
|
|
43
|
+
if (typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://') || value.startsWith('/'))) {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
if (value && typeof value === 'object' && 'src' in (value as Record<string, unknown>)) {
|
|
47
|
+
return String((value as Record<string, unknown>).src ?? '');
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const resolvedName = $derived.by(() => {
|
|
53
|
+
if (name !== undefined) return name;
|
|
54
|
+
if (typeof value === 'string' && !value.startsWith('http://') && !value.startsWith('https://') && !value.startsWith('/')) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
if (value && typeof value === 'object' && 'name' in (value as Record<string, unknown>)) {
|
|
58
|
+
return String((value as Record<string, unknown>).name ?? '');
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function getInitials(text?: string | null): string {
|
|
64
|
+
const trimmed = text?.trim();
|
|
65
|
+
if (!trimmed) return '?';
|
|
66
|
+
const parts = trimmed.split(/\s+/);
|
|
67
|
+
if (parts.length >= 2) {
|
|
68
|
+
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
69
|
+
}
|
|
70
|
+
return trimmed.slice(0, 2).toUpperCase();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const initials = $derived(getInitials(resolvedName));
|
|
74
|
+
const hasContent = $derived(Boolean(resolvedSrc || resolvedName?.trim()));
|
|
75
|
+
const hasError = $derived(error.length > 0);
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
{#if mode === 'show'}
|
|
79
|
+
{#if !hasContent}
|
|
80
|
+
<span class="lite-text-muted lite-text-sm {className}">{nullLabel}</span>
|
|
81
|
+
{:else}
|
|
82
|
+
<span class="lite-avatar-wrapper {className}">
|
|
83
|
+
<span class="lite-avatar-container">
|
|
84
|
+
<span class="lite-avatar lite-avatar-{size} lite-avatar-{shape}">
|
|
85
|
+
{#if resolvedSrc}
|
|
86
|
+
<img src={resolvedSrc} alt={resolvedName?.trim() || 'Avatar'} class="lite-avatar-img" />
|
|
87
|
+
{:else}
|
|
88
|
+
<span class="lite-avatar-initials">{initials}</span>
|
|
89
|
+
{/if}
|
|
90
|
+
</span>
|
|
91
|
+
{#if status}
|
|
92
|
+
<span
|
|
93
|
+
class="lite-avatar-dot lite-avatar-dot-{size} lite-avatar-status-{status}"
|
|
94
|
+
title={`Status: ${status}`}
|
|
95
|
+
></span>
|
|
96
|
+
{/if}
|
|
97
|
+
</span>
|
|
98
|
+
{#if showName && (resolvedName || subtitle)}
|
|
99
|
+
<span class="lite-avatar-text-block">
|
|
100
|
+
{#if resolvedName}
|
|
101
|
+
<span class="lite-avatar-name">{resolvedName}</span>
|
|
102
|
+
{/if}
|
|
103
|
+
{#if subtitle}
|
|
104
|
+
<span class="lite-avatar-subtitle">{subtitle}</span>
|
|
105
|
+
{/if}
|
|
106
|
+
</span>
|
|
107
|
+
{/if}
|
|
108
|
+
</span>
|
|
109
|
+
{/if}
|
|
110
|
+
{:else}
|
|
111
|
+
<div>
|
|
112
|
+
<input
|
|
113
|
+
type="text"
|
|
114
|
+
name={field?.key ?? 'avatar'}
|
|
115
|
+
id={field?.key ?? 'avatar'}
|
|
116
|
+
value={String(resolvedSrc ?? resolvedName ?? value ?? '')}
|
|
117
|
+
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
118
|
+
placeholder={field?.label ?? 'Avatar URL or name'}
|
|
119
|
+
{...field?.required ? { required: true } : {}}
|
|
120
|
+
/>
|
|
121
|
+
{#if hasError}
|
|
122
|
+
{#each error as err, _i (_i)}
|
|
123
|
+
<div class="lite-error-text">{err}</div>
|
|
124
|
+
{/each}
|
|
125
|
+
{/if}
|
|
126
|
+
</div>
|
|
127
|
+
{/if}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type LiteAvatarStatus = 'online' | 'offline' | 'busy' | 'away' | 'success' | 'warning' | 'error' | 'neutral';
|
|
2
|
+
export type LiteAvatarSize = 'xs' | 'sm' | 'default' | 'lg';
|
|
3
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
4
|
+
interface Props {
|
|
5
|
+
field?: FieldDefinition;
|
|
6
|
+
value?: unknown;
|
|
7
|
+
error?: string[];
|
|
8
|
+
mode?: 'show' | 'edit' | 'create';
|
|
9
|
+
src?: string | null;
|
|
10
|
+
name?: string | null;
|
|
11
|
+
subtitle?: string | null;
|
|
12
|
+
status?: LiteAvatarStatus;
|
|
13
|
+
size?: LiteAvatarSize;
|
|
14
|
+
shape?: 'circle' | 'square';
|
|
15
|
+
showName?: boolean;
|
|
16
|
+
nullLabel?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const LiteAvatarField: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type LiteAvatarField = ReturnType<typeof LiteAvatarField>;
|
|
21
|
+
export default LiteAvatarField;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
3
|
+
import { t } from '@svadmin/core/i18n';
|
|
4
|
+
|
|
5
|
+
export interface CascaderOption {
|
|
6
|
+
value: string | number;
|
|
7
|
+
label: string;
|
|
8
|
+
children?: CascaderOption[];
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
isLeaf?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
field?: FieldDefinition;
|
|
15
|
+
name?: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
value?: (string | number)[];
|
|
18
|
+
options?: CascaderOption[];
|
|
19
|
+
separator?: string;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
error?: string[];
|
|
24
|
+
mode?: 'show' | 'edit' | 'create';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
field,
|
|
29
|
+
name = field?.key ?? 'cascader',
|
|
30
|
+
label = field?.label,
|
|
31
|
+
value = [],
|
|
32
|
+
options = [],
|
|
33
|
+
separator = ' / ',
|
|
34
|
+
placeholder,
|
|
35
|
+
disabled = false,
|
|
36
|
+
required = field?.required ?? false,
|
|
37
|
+
error = [],
|
|
38
|
+
mode = 'show',
|
|
39
|
+
}: Props = $props();
|
|
40
|
+
|
|
41
|
+
const hasError = $derived(error.length > 0);
|
|
42
|
+
|
|
43
|
+
interface FlatPathOption {
|
|
44
|
+
pathValue: string;
|
|
45
|
+
pathLabels: string[];
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function generatePaths(
|
|
50
|
+
nodes: CascaderOption[],
|
|
51
|
+
currentValues: (string | number)[] = [],
|
|
52
|
+
currentLabels: string[] = []
|
|
53
|
+
): FlatPathOption[] {
|
|
54
|
+
const paths: FlatPathOption[] = [];
|
|
55
|
+
for (const node of nodes) {
|
|
56
|
+
const nextValues = [...currentValues, node.value];
|
|
57
|
+
const nextLabels = [...currentLabels, node.label];
|
|
58
|
+
|
|
59
|
+
if (node.children && node.children.length > 0) {
|
|
60
|
+
paths.push(...generatePaths(node.children, nextValues, nextLabels));
|
|
61
|
+
} else {
|
|
62
|
+
paths.push({
|
|
63
|
+
pathValue: nextValues.join('/'),
|
|
64
|
+
pathLabels: nextLabels,
|
|
65
|
+
disabled: node.disabled,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return paths;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const allPaths = $derived(generatePaths(options));
|
|
73
|
+
const currentPathString = $derived(Array.isArray(value) ? value.join('/') : String(value ?? ''));
|
|
74
|
+
|
|
75
|
+
function formatDisplay(v: (string | number)[]): string {
|
|
76
|
+
if (!v || v.length === 0) return '—';
|
|
77
|
+
const str = v.join('/');
|
|
78
|
+
const matched = allPaths.find((p) => p.pathValue === str);
|
|
79
|
+
if (matched) return matched.pathLabels.join(separator);
|
|
80
|
+
return v.join(separator);
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
{#if mode === 'show'}
|
|
85
|
+
<span class="lite-badge">{formatDisplay(value)}</span>
|
|
86
|
+
{:else}
|
|
87
|
+
<div class="lite-form-group">
|
|
88
|
+
{#if label}
|
|
89
|
+
<label class="lite-label" for={name}>
|
|
90
|
+
{label}
|
|
91
|
+
{#if required}<span class="required">*</span>{/if}
|
|
92
|
+
</label>
|
|
93
|
+
{/if}
|
|
94
|
+
<select
|
|
95
|
+
id={name}
|
|
96
|
+
{name}
|
|
97
|
+
class="lite-select {hasError ? 'lite-input-error' : ''}"
|
|
98
|
+
{disabled}
|
|
99
|
+
{required}
|
|
100
|
+
value={currentPathString}
|
|
101
|
+
>
|
|
102
|
+
<option value="">-- {placeholder || t('common.select') || 'Select Path'} --</option>
|
|
103
|
+
{#each allPaths as p (p.pathValue)}
|
|
104
|
+
<option
|
|
105
|
+
value={p.pathValue}
|
|
106
|
+
disabled={p.disabled}
|
|
107
|
+
selected={p.pathValue === currentPathString}
|
|
108
|
+
>
|
|
109
|
+
{p.pathLabels.join(separator)}
|
|
110
|
+
</option>
|
|
111
|
+
{/each}
|
|
112
|
+
</select>
|
|
113
|
+
{#if hasError}
|
|
114
|
+
{#each error as err, i (i)}
|
|
115
|
+
<div class="lite-error-text">{err}</div>
|
|
116
|
+
{/each}
|
|
117
|
+
{/if}
|
|
118
|
+
</div>
|
|
119
|
+
{/if}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
2
|
+
export interface CascaderOption {
|
|
3
|
+
value: string | number;
|
|
4
|
+
label: string;
|
|
5
|
+
children?: CascaderOption[];
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
isLeaf?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface Props {
|
|
10
|
+
field?: FieldDefinition;
|
|
11
|
+
name?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
value?: (string | number)[];
|
|
14
|
+
options?: CascaderOption[];
|
|
15
|
+
separator?: string;
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
error?: string[];
|
|
20
|
+
mode?: 'show' | 'edit' | 'create';
|
|
21
|
+
}
|
|
22
|
+
declare const LiteCascader: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type LiteCascader = ReturnType<typeof LiteCascader>;
|
|
24
|
+
export default LiteCascader;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
field?: FieldDefinition;
|
|
6
|
+
value?: string | number | Record<string, unknown> | unknown[] | null;
|
|
7
|
+
language?: string;
|
|
8
|
+
copyable?: boolean;
|
|
9
|
+
title?: string;
|
|
10
|
+
nullLabel?: string;
|
|
11
|
+
maxHeight?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
error?: string[];
|
|
14
|
+
mode?: 'show' | 'edit' | 'create';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
field,
|
|
19
|
+
value,
|
|
20
|
+
language,
|
|
21
|
+
nullLabel = '—',
|
|
22
|
+
maxHeight = '200px',
|
|
23
|
+
class: className = '',
|
|
24
|
+
error = [],
|
|
25
|
+
mode = 'show',
|
|
26
|
+
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
const formattedCode = $derived.by(() => {
|
|
29
|
+
if (value == null || value === '') return '';
|
|
30
|
+
if (typeof value === 'string') return value;
|
|
31
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
32
|
+
try {
|
|
33
|
+
return JSON.stringify(value, null, 2);
|
|
34
|
+
} catch {
|
|
35
|
+
return String(value);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const hasError = $derived(error.length > 0);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
{#if mode === 'show'}
|
|
43
|
+
{#if !formattedCode}
|
|
44
|
+
<span class="lite-text-muted lite-text-sm {className}">{nullLabel}</span>
|
|
45
|
+
{:else}
|
|
46
|
+
<div class="lite-code-block {className}">
|
|
47
|
+
{#if language}
|
|
48
|
+
<div class="lite-code-header">
|
|
49
|
+
<span class="lite-badge">{language.toUpperCase()}</span>
|
|
50
|
+
</div>
|
|
51
|
+
{/if}
|
|
52
|
+
<pre class="lite-code-pre" style="max-height: {maxHeight};"><code>{formattedCode}</code></pre>
|
|
53
|
+
</div>
|
|
54
|
+
{/if}
|
|
55
|
+
{:else}
|
|
56
|
+
<div>
|
|
57
|
+
<textarea
|
|
58
|
+
name={field?.key ?? 'code'}
|
|
59
|
+
id={field?.key ?? 'code'}
|
|
60
|
+
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
61
|
+
style="min-height: 120px; font-family: monospace; font-size: 12px;"
|
|
62
|
+
placeholder={field?.label ?? 'Code / content'}
|
|
63
|
+
{...field?.required ? { required: true } : {}}
|
|
64
|
+
>{formattedCode}</textarea>
|
|
65
|
+
{#if hasError}
|
|
66
|
+
{#each error as err, _i (_i)}
|
|
67
|
+
<div class="lite-error-text">{err}</div>
|
|
68
|
+
{/each}
|
|
69
|
+
{/if}
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
2
|
+
interface Props {
|
|
3
|
+
field?: FieldDefinition;
|
|
4
|
+
value?: string | number | Record<string, unknown> | unknown[] | null;
|
|
5
|
+
language?: string;
|
|
6
|
+
copyable?: boolean;
|
|
7
|
+
title?: string;
|
|
8
|
+
nullLabel?: string;
|
|
9
|
+
maxHeight?: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
error?: string[];
|
|
12
|
+
mode?: 'show' | 'edit' | 'create';
|
|
13
|
+
}
|
|
14
|
+
declare const LiteCodeField: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type LiteCodeField = ReturnType<typeof LiteCodeField>;
|
|
16
|
+
export default LiteCodeField;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
field?: FieldDefinition;
|
|
6
|
+
value?: string | number | null;
|
|
7
|
+
displayValue?: string;
|
|
8
|
+
masked?: boolean;
|
|
9
|
+
monospace?: boolean;
|
|
10
|
+
copyable?: boolean;
|
|
11
|
+
title?: string;
|
|
12
|
+
nullLabel?: string;
|
|
13
|
+
oncopy?: (value: string) => void;
|
|
14
|
+
class?: string;
|
|
15
|
+
error?: string[];
|
|
16
|
+
mode?: 'show' | 'edit' | 'create';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
field,
|
|
21
|
+
value,
|
|
22
|
+
displayValue,
|
|
23
|
+
masked = false,
|
|
24
|
+
monospace = true,
|
|
25
|
+
nullLabel = '—',
|
|
26
|
+
class: className = '',
|
|
27
|
+
error = [],
|
|
28
|
+
mode = 'show',
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
function maskText(str: string): string {
|
|
32
|
+
if (str.length <= 8) return '****';
|
|
33
|
+
return `${str.slice(0, 4)}...${str.slice(-4)}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const rawString = $derived(value != null && value !== '' ? String(value) : '');
|
|
37
|
+
const displayText = $derived.by(() => {
|
|
38
|
+
if (!rawString) return nullLabel;
|
|
39
|
+
if (displayValue) return displayValue;
|
|
40
|
+
if (masked) return maskText(rawString);
|
|
41
|
+
return rawString;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const hasError = $derived(error.length > 0);
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
{#if mode === 'show'}
|
|
48
|
+
{#if !rawString}
|
|
49
|
+
<span class="lite-text-muted lite-text-sm {className}">{nullLabel}</span>
|
|
50
|
+
{:else}
|
|
51
|
+
<span class="lite-copy-field {className}">
|
|
52
|
+
<span class="lite-copy-text {monospace ? 'lite-font-mono' : ''}">
|
|
53
|
+
{displayText}
|
|
54
|
+
</span>
|
|
55
|
+
</span>
|
|
56
|
+
{/if}
|
|
57
|
+
{:else}
|
|
58
|
+
<div>
|
|
59
|
+
<input
|
|
60
|
+
type="text"
|
|
61
|
+
name={field?.key ?? 'copy_text'}
|
|
62
|
+
id={field?.key ?? 'copy_text'}
|
|
63
|
+
value={rawString}
|
|
64
|
+
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
65
|
+
placeholder={field?.label ?? 'Value'}
|
|
66
|
+
{...field?.required ? { required: true } : {}}
|
|
67
|
+
/>
|
|
68
|
+
{#if hasError}
|
|
69
|
+
{#each error as err, _i (_i)}
|
|
70
|
+
<div class="lite-error-text">{err}</div>
|
|
71
|
+
{/each}
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
74
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FieldDefinition } from '@svadmin/core';
|
|
2
|
+
interface Props {
|
|
3
|
+
field?: FieldDefinition;
|
|
4
|
+
value?: string | number | null;
|
|
5
|
+
displayValue?: string;
|
|
6
|
+
masked?: boolean;
|
|
7
|
+
monospace?: boolean;
|
|
8
|
+
copyable?: boolean;
|
|
9
|
+
title?: string;
|
|
10
|
+
nullLabel?: string;
|
|
11
|
+
oncopy?: (value: string) => void;
|
|
12
|
+
class?: string;
|
|
13
|
+
error?: string[];
|
|
14
|
+
mode?: 'show' | 'edit' | 'create';
|
|
15
|
+
}
|
|
16
|
+
declare const LiteCopyField: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type LiteCopyField = ReturnType<typeof LiteCopyField>;
|
|
18
|
+
export default LiteCopyField;
|