@duskmoon-dev/el-cascader 0.5.0 → 0.6.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/dist/cjs/index.js +31 -211
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/register.js +31 -211
- package/dist/cjs/register.js.map +3 -3
- package/dist/esm/index.js +29 -209
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/register.js +29 -209
- package/dist/esm/register.js.map +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-cascader.d.ts +2 -2
- package/dist/types/el-dm-cascader.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/duskmoon-core.d.ts +7 -0
- package/src/el-dm-cascader.test.ts +232 -0
- package/src/el-dm-cascader.ts +32 -210
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A multi-panel cascading selection component following Ant Design patterns.
|
|
5
5
|
*/
|
|
6
|
-
import { BaseElement } from '@duskmoon-dev/el-
|
|
7
|
-
import type { Size, ValidationState } from '@duskmoon-dev/el-
|
|
6
|
+
import { BaseElement } from '@duskmoon-dev/el-base';
|
|
7
|
+
import type { Size, ValidationState } from '@duskmoon-dev/el-base';
|
|
8
8
|
/**
|
|
9
9
|
* Cascader option structure
|
|
10
10
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"el-dm-cascader.d.ts","sourceRoot":"","sources":["../../src/el-dm-cascader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAO,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"el-dm-cascader.d.ts","sourceRoot":"","sources":["../../src/el-dm-cascader.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duskmoon-dev/el-cascader",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "DuskMoon Cascader custom element for multi-level cascading selection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -25,16 +25,17 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"prebuild": "bun run clean",
|
|
27
27
|
"build": "bun run build:esm && bun run build:cjs && bun run build:types",
|
|
28
|
-
"build:esm": "bun build ./src/index.ts ./src/register.ts --outdir ./dist/esm --format esm --sourcemap --external @duskmoon-dev/el-core",
|
|
29
|
-
"build:cjs": "bun build ./src/index.ts ./src/register.ts --outdir ./dist/cjs --format cjs --sourcemap --external @duskmoon-dev/el-core",
|
|
28
|
+
"build:esm": "bun build ./src/index.ts ./src/register.ts --outdir ./dist/esm --format esm --sourcemap --external @duskmoon-dev/el-base --external @duskmoon-dev/core",
|
|
29
|
+
"build:cjs": "bun build ./src/index.ts ./src/register.ts --outdir ./dist/cjs --format cjs --sourcemap --external @duskmoon-dev/el-base --external @duskmoon-dev/core",
|
|
30
30
|
"build:types": "tsc --emitDeclarationOnly --outDir ./dist/types",
|
|
31
|
-
"dev": "bun build ./src/index.ts --outdir ./dist/esm --format esm --sourcemap --external @duskmoon-dev/el-core --watch",
|
|
31
|
+
"dev": "bun build ./src/index.ts --outdir ./dist/esm --format esm --sourcemap --external @duskmoon-dev/el-base --external @duskmoon-dev/core --watch",
|
|
32
32
|
"clean": "rm -rf dist",
|
|
33
33
|
"test": "bun test",
|
|
34
34
|
"typecheck": "tsc --noEmit"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@duskmoon-dev/el-
|
|
37
|
+
"@duskmoon-dev/el-base": "0.5.1",
|
|
38
|
+
"@duskmoon-dev/core": "^1.10.1"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"typescript": "^5.8.3"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { expect, test, describe, beforeEach, afterEach } from 'bun:test';
|
|
2
|
+
import { ElDmCascader, register } from './index';
|
|
3
|
+
import type { CascaderOption } from './index';
|
|
4
|
+
|
|
5
|
+
register();
|
|
6
|
+
|
|
7
|
+
const sampleOptions: CascaderOption[] = [
|
|
8
|
+
{ value: 'fruits', label: 'Fruits', children: [
|
|
9
|
+
{ value: 'apple', label: 'Apple' },
|
|
10
|
+
{ value: 'banana', label: 'Banana' },
|
|
11
|
+
]},
|
|
12
|
+
{ value: 'vegs', label: 'Vegetables', children: [
|
|
13
|
+
{ value: 'carrot', label: 'Carrot' },
|
|
14
|
+
]},
|
|
15
|
+
{ value: 'grains', label: 'Grains' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function createCascader(props: Partial<ElDmCascader> = {}): ElDmCascader {
|
|
19
|
+
const el = document.createElement('el-dm-cascader') as ElDmCascader;
|
|
20
|
+
Object.assign(el, props);
|
|
21
|
+
return el;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('ElDmCascader', () => {
|
|
25
|
+
let container: HTMLDivElement;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
container = document.createElement('div');
|
|
29
|
+
document.body.appendChild(container);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
container.remove();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// --- Registration ---
|
|
37
|
+
test('is defined', () => {
|
|
38
|
+
expect(customElements.get('el-dm-cascader')).toBe(ElDmCascader);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// --- Rendering ---
|
|
42
|
+
test('creates a shadow root', () => {
|
|
43
|
+
const el = createCascader();
|
|
44
|
+
container.appendChild(el);
|
|
45
|
+
expect(el.shadowRoot).toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('has cascader trigger', () => {
|
|
49
|
+
const el = createCascader();
|
|
50
|
+
container.appendChild(el);
|
|
51
|
+
const trigger = el.shadowRoot?.querySelector('.cascader-trigger');
|
|
52
|
+
expect(trigger).toBeDefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('shows placeholder text', () => {
|
|
56
|
+
const el = createCascader({ placeholder: 'Pick one' });
|
|
57
|
+
container.appendChild(el);
|
|
58
|
+
const placeholder = el.shadowRoot?.querySelector('.cascader-placeholder');
|
|
59
|
+
expect(placeholder?.textContent).toContain('Pick one');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('shows default placeholder', () => {
|
|
63
|
+
const el = createCascader();
|
|
64
|
+
container.appendChild(el);
|
|
65
|
+
const placeholder = el.shadowRoot?.querySelector('.cascader-placeholder');
|
|
66
|
+
expect(placeholder?.textContent).toContain('Select...');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('has dropdown element', () => {
|
|
70
|
+
const el = createCascader();
|
|
71
|
+
container.appendChild(el);
|
|
72
|
+
const dropdown = el.shadowRoot?.querySelector('.cascader-dropdown');
|
|
73
|
+
expect(dropdown).toBeDefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('has panels container', () => {
|
|
77
|
+
const el = createCascader();
|
|
78
|
+
container.appendChild(el);
|
|
79
|
+
const panels = el.shadowRoot?.querySelector('.cascader-panels');
|
|
80
|
+
expect(panels).toBeDefined();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('has chevron icon', () => {
|
|
84
|
+
const el = createCascader();
|
|
85
|
+
container.appendChild(el);
|
|
86
|
+
const chevron = el.shadowRoot?.querySelector('.cascader-chevron');
|
|
87
|
+
expect(chevron).toBeDefined();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// --- Properties ---
|
|
91
|
+
test('reflects value attribute', () => {
|
|
92
|
+
const el = createCascader({ value: '["fruits","apple"]' });
|
|
93
|
+
container.appendChild(el);
|
|
94
|
+
expect(el.getAttribute('value')).toBe('["fruits","apple"]');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('reflects disabled attribute', () => {
|
|
98
|
+
const el = createCascader({ disabled: true });
|
|
99
|
+
container.appendChild(el);
|
|
100
|
+
expect(el.hasAttribute('disabled')).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('reflects multiple attribute', () => {
|
|
104
|
+
const el = createCascader({ multiple: true });
|
|
105
|
+
container.appendChild(el);
|
|
106
|
+
expect(el.hasAttribute('multiple')).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('reflects searchable attribute', () => {
|
|
110
|
+
const el = createCascader({ searchable: true });
|
|
111
|
+
container.appendChild(el);
|
|
112
|
+
expect(el.hasAttribute('searchable')).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('reflects clearable attribute', () => {
|
|
116
|
+
const el = createCascader({ clearable: true });
|
|
117
|
+
container.appendChild(el);
|
|
118
|
+
expect(el.hasAttribute('clearable')).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('reflects changeOnSelect attribute', () => {
|
|
122
|
+
const el = createCascader({ changeOnSelect: true });
|
|
123
|
+
container.appendChild(el);
|
|
124
|
+
expect(el.hasAttribute('change-on-select')).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('defaults expandTrigger to click', () => {
|
|
128
|
+
const el = createCascader();
|
|
129
|
+
container.appendChild(el);
|
|
130
|
+
expect(el.expandTrigger).toBe('click');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('defaults separator to " / "', () => {
|
|
134
|
+
const el = createCascader();
|
|
135
|
+
container.appendChild(el);
|
|
136
|
+
expect(el.separator).toBe(' / ');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('defaults size to md', () => {
|
|
140
|
+
const el = createCascader();
|
|
141
|
+
container.appendChild(el);
|
|
142
|
+
expect(el.size).toBe('md');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('reflects size attribute', () => {
|
|
146
|
+
const el = createCascader({ size: 'sm' } as Partial<ElDmCascader>);
|
|
147
|
+
container.appendChild(el);
|
|
148
|
+
expect(el.getAttribute('size')).toBe('sm');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// --- Public methods ---
|
|
152
|
+
test('has setOptions method', () => {
|
|
153
|
+
const el = createCascader();
|
|
154
|
+
container.appendChild(el);
|
|
155
|
+
expect(typeof el.setOptions).toBe('function');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('has setLoadData method', () => {
|
|
159
|
+
const el = createCascader();
|
|
160
|
+
container.appendChild(el);
|
|
161
|
+
expect(typeof el.setLoadData).toBe('function');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('setOptions renders options in first panel', () => {
|
|
165
|
+
const el = createCascader();
|
|
166
|
+
container.appendChild(el);
|
|
167
|
+
el.setOptions(sampleOptions);
|
|
168
|
+
const options = el.shadowRoot?.querySelectorAll('.cascader-option');
|
|
169
|
+
expect(options?.length).toBeGreaterThanOrEqual(3);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// --- Accessibility ---
|
|
173
|
+
test('has combobox role', () => {
|
|
174
|
+
const el = createCascader();
|
|
175
|
+
container.appendChild(el);
|
|
176
|
+
const combobox = el.shadowRoot?.querySelector('[role="combobox"]');
|
|
177
|
+
expect(combobox).toBeDefined();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('has aria-expanded', () => {
|
|
181
|
+
const el = createCascader();
|
|
182
|
+
container.appendChild(el);
|
|
183
|
+
const trigger = el.shadowRoot?.querySelector('[aria-expanded]');
|
|
184
|
+
expect(trigger).toBeDefined();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// --- Disabled ---
|
|
188
|
+
test('trigger is disabled when component disabled', () => {
|
|
189
|
+
const el = createCascader({ disabled: true });
|
|
190
|
+
container.appendChild(el);
|
|
191
|
+
const trigger = el.shadowRoot?.querySelector('.cascader-trigger');
|
|
192
|
+
expect(trigger?.hasAttribute('disabled')).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// --- Size via host attribute (styled with :host([size="sm/lg"])) ---
|
|
196
|
+
test('host has size=sm attribute', () => {
|
|
197
|
+
const el = createCascader({ size: 'sm' } as Partial<ElDmCascader>);
|
|
198
|
+
container.appendChild(el);
|
|
199
|
+
expect(el.getAttribute('size')).toBe('sm');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('host has size=lg attribute', () => {
|
|
203
|
+
const el = createCascader({ size: 'lg' } as Partial<ElDmCascader>);
|
|
204
|
+
container.appendChild(el);
|
|
205
|
+
expect(el.getAttribute('size')).toBe('lg');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// --- Validation ---
|
|
209
|
+
test('reflects validationState attribute', () => {
|
|
210
|
+
const el = createCascader({ validationState: 'invalid' } as Partial<ElDmCascader>);
|
|
211
|
+
container.appendChild(el);
|
|
212
|
+
expect(el.getAttribute('validation-state')).toBe('invalid');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// --- Options with children show expand indicator ---
|
|
216
|
+
test('options with children have expand indicator', () => {
|
|
217
|
+
const el = createCascader();
|
|
218
|
+
container.appendChild(el);
|
|
219
|
+
el.setOptions(sampleOptions);
|
|
220
|
+
const expandable = el.shadowRoot?.querySelectorAll('.cascader-option-arrow');
|
|
221
|
+
// fruits and vegs have children = 2 arrows
|
|
222
|
+
expect(expandable?.length).toBeGreaterThanOrEqual(2);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// --- Searchable ---
|
|
226
|
+
test('shows search input when searchable', () => {
|
|
227
|
+
const el = createCascader({ searchable: true });
|
|
228
|
+
container.appendChild(el);
|
|
229
|
+
const searchInput = el.shadowRoot?.querySelector('.cascader-search-input');
|
|
230
|
+
expect(searchInput).toBeDefined();
|
|
231
|
+
});
|
|
232
|
+
});
|
package/src/el-dm-cascader.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* A multi-panel cascading selection component following Ant Design patterns.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { BaseElement, css } from '@duskmoon-dev/el-
|
|
8
|
-
import type { Size, ValidationState } from '@duskmoon-dev/el-
|
|
7
|
+
import { BaseElement, css } from '@duskmoon-dev/el-base';
|
|
8
|
+
import type { Size, ValidationState } from '@duskmoon-dev/el-base';
|
|
9
|
+
import { css as cascaderCSS } from '@duskmoon-dev/core/components/cascader';
|
|
9
10
|
|
|
10
11
|
// Icons with explicit dimensions for proper rendering
|
|
11
12
|
const chevronDownIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>`;
|
|
@@ -59,67 +60,41 @@ interface SearchResult {
|
|
|
59
60
|
pathValues: string[];
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
//
|
|
63
|
+
// Strip @layer wrapper from core CSS for Shadow DOM usage
|
|
64
|
+
const coreStyles = cascaderCSS.replace(/@layer\s+components\s*\{/, '').replace(/\}\s*$/, '');
|
|
65
|
+
|
|
66
|
+
// Styles: core CSS + element-specific overrides
|
|
63
67
|
const styles = css`
|
|
64
68
|
:host {
|
|
65
69
|
display: inline-block;
|
|
66
70
|
width: 100%;
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
${coreStyles}
|
|
74
|
+
|
|
75
|
+
/* Override: block display for full-width behavior */
|
|
69
76
|
.cascader {
|
|
70
|
-
|
|
71
|
-
width: 100%;
|
|
77
|
+
display: block;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
/*
|
|
80
|
+
/* Override: trigger sizing & gap */
|
|
75
81
|
.cascader-trigger {
|
|
76
|
-
display: flex;
|
|
77
|
-
align-items: center;
|
|
78
82
|
gap: 0.5rem;
|
|
79
|
-
width: 100%;
|
|
80
83
|
min-height: 2.75rem;
|
|
81
84
|
padding: 0.5rem 0.75rem;
|
|
82
|
-
font-size: var(--font-size-md, 1rem);
|
|
83
|
-
line-height: 1.5;
|
|
84
|
-
color: var(--color-on-surface);
|
|
85
|
-
background-color: var(--color-surface);
|
|
86
|
-
border: 1px solid var(--color-outline);
|
|
87
|
-
border-radius: var(--radius-md, 0.5rem);
|
|
88
|
-
cursor: pointer;
|
|
89
|
-
transition: border-color 150ms ease, box-shadow 150ms ease;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.cascader-trigger:hover:not(:disabled) {
|
|
93
|
-
border-color: var(--color-on-surface-variant);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.cascader-trigger:focus {
|
|
97
|
-
outline: none;
|
|
98
|
-
border-color: var(--color-primary);
|
|
99
|
-
box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 15%, transparent);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.cascader-trigger:disabled {
|
|
103
|
-
cursor: not-allowed;
|
|
104
|
-
opacity: 0.5;
|
|
105
|
-
background-color: var(--color-surface-container);
|
|
106
85
|
}
|
|
107
86
|
|
|
108
|
-
/*
|
|
87
|
+
/* Override: value display as block text */
|
|
109
88
|
.cascader-value {
|
|
110
|
-
|
|
111
|
-
overflow: hidden;
|
|
112
|
-
text-overflow: ellipsis;
|
|
113
|
-
white-space: nowrap;
|
|
89
|
+
display: block;
|
|
114
90
|
text-align: left;
|
|
115
91
|
}
|
|
116
92
|
|
|
117
93
|
.cascader-placeholder {
|
|
118
|
-
color: var(--color-on-surface-variant);
|
|
119
94
|
opacity: 0.7;
|
|
120
95
|
}
|
|
121
96
|
|
|
122
|
-
/* Tags
|
|
97
|
+
/* Tags (not in core) */
|
|
123
98
|
.cascader-tags {
|
|
124
99
|
display: flex;
|
|
125
100
|
flex-wrap: wrap;
|
|
@@ -179,81 +154,20 @@ const styles = css`
|
|
|
179
154
|
color: var(--color-on-surface-variant);
|
|
180
155
|
}
|
|
181
156
|
|
|
182
|
-
/*
|
|
183
|
-
.cascader-arrow
|
|
184
|
-
display: inline-flex;
|
|
185
|
-
align-items: center;
|
|
186
|
-
justify-content: center;
|
|
187
|
-
width: 20px;
|
|
188
|
-
height: 20px;
|
|
189
|
-
flex-shrink: 0;
|
|
190
|
-
color: var(--color-on-surface-variant);
|
|
191
|
-
transition: transform 150ms ease;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.cascader-arrow svg {
|
|
195
|
-
width: 16px;
|
|
196
|
-
height: 16px;
|
|
197
|
-
display: block;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.cascader.open .cascader-arrow {
|
|
201
|
-
transform: rotate(180deg);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.cascader-clear {
|
|
205
|
-
display: inline-flex;
|
|
206
|
-
align-items: center;
|
|
207
|
-
justify-content: center;
|
|
208
|
-
width: 20px;
|
|
209
|
-
height: 20px;
|
|
210
|
-
padding: 0;
|
|
211
|
-
color: var(--color-on-surface-variant);
|
|
212
|
-
background-color: transparent;
|
|
213
|
-
border-radius: 50%;
|
|
214
|
-
cursor: pointer;
|
|
215
|
-
flex-shrink: 0;
|
|
216
|
-
transition: background-color 150ms ease;
|
|
217
|
-
}
|
|
218
|
-
|
|
157
|
+
/* Override: arrow/clear SVG sizing */
|
|
158
|
+
.cascader-arrow svg,
|
|
219
159
|
.cascader-clear svg {
|
|
220
|
-
width: 14px;
|
|
221
|
-
height: 14px;
|
|
222
160
|
display: block;
|
|
223
161
|
}
|
|
224
162
|
|
|
225
|
-
|
|
226
|
-
background-color: var(--color-surface-container-high);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/* Dropdown - uses Popover API (top-layer requires position: fixed) */
|
|
163
|
+
/* Override: dropdown uses Popover API (top-layer requires position: fixed) */
|
|
230
164
|
.cascader-dropdown {
|
|
231
165
|
position: fixed;
|
|
232
|
-
margin: 0;
|
|
233
|
-
padding: 0;
|
|
234
|
-
border: 1px solid var(--color-outline-variant);
|
|
235
|
-
border-radius: var(--radius-md, 0.5rem);
|
|
236
|
-
background-color: var(--color-surface);
|
|
237
|
-
box-shadow: var(--shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
|
|
238
|
-
overflow: hidden;
|
|
239
|
-
display: none;
|
|
240
166
|
flex-direction: column;
|
|
241
167
|
z-index: 1000;
|
|
242
168
|
}
|
|
243
169
|
|
|
244
|
-
|
|
245
|
-
display: flex;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/* Search */
|
|
249
|
-
.cascader-search {
|
|
250
|
-
display: flex;
|
|
251
|
-
align-items: center;
|
|
252
|
-
gap: 0.5rem;
|
|
253
|
-
padding: 0.5rem;
|
|
254
|
-
border-bottom: 1px solid var(--color-outline-variant);
|
|
255
|
-
}
|
|
256
|
-
|
|
170
|
+
/* Search icon (not in core) */
|
|
257
171
|
.cascader-search-icon {
|
|
258
172
|
display: inline-flex;
|
|
259
173
|
align-items: center;
|
|
@@ -270,86 +184,16 @@ const styles = css`
|
|
|
270
184
|
display: block;
|
|
271
185
|
}
|
|
272
186
|
|
|
273
|
-
|
|
274
|
-
flex: 1;
|
|
275
|
-
padding: 0.375rem 0.5rem;
|
|
276
|
-
font-size: var(--font-size-sm, 0.875rem);
|
|
277
|
-
color: var(--color-on-surface);
|
|
278
|
-
background-color: var(--color-surface-container);
|
|
279
|
-
border: none;
|
|
280
|
-
border-radius: var(--radius-sm, 0.25rem);
|
|
281
|
-
outline: none;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.cascader-search-input:focus {
|
|
285
|
-
background-color: var(--color-surface-container-high);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.cascader-search-input::placeholder {
|
|
289
|
-
color: var(--color-on-surface-variant);
|
|
290
|
-
opacity: 0.7;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/* Panels Container */
|
|
187
|
+
/* Override: panel max-height */
|
|
294
188
|
.cascader-panels {
|
|
295
|
-
display: flex;
|
|
296
189
|
max-height: 18rem;
|
|
297
190
|
}
|
|
298
191
|
|
|
299
|
-
/* Panel */
|
|
300
192
|
.cascader-panel {
|
|
301
|
-
display: flex;
|
|
302
|
-
flex-direction: column;
|
|
303
|
-
min-width: 10rem;
|
|
304
|
-
max-width: 14rem;
|
|
305
193
|
max-height: 18rem;
|
|
306
|
-
overflow-y: auto;
|
|
307
|
-
border-right: 1px solid var(--color-outline-variant);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
.cascader-panel:last-child {
|
|
311
|
-
border-right: none;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
.cascader-panel-options {
|
|
315
|
-
padding: 0.25rem;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/* Option */
|
|
319
|
-
.cascader-option {
|
|
320
|
-
display: flex;
|
|
321
|
-
align-items: center;
|
|
322
|
-
gap: 0.5rem;
|
|
323
|
-
width: 100%;
|
|
324
|
-
padding: 0.5rem 0.75rem;
|
|
325
|
-
font-size: var(--font-size-sm, 0.875rem);
|
|
326
|
-
color: var(--color-on-surface);
|
|
327
|
-
background-color: transparent;
|
|
328
|
-
border: none;
|
|
329
|
-
border-radius: var(--radius-sm, 0.25rem);
|
|
330
|
-
cursor: pointer;
|
|
331
|
-
text-align: left;
|
|
332
|
-
transition: background-color 150ms ease;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
.cascader-option:hover:not(.disabled) {
|
|
336
|
-
background-color: var(--color-surface-container);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.cascader-option.active {
|
|
340
|
-
background-color: var(--color-surface-container-high);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
.cascader-option.selected {
|
|
344
|
-
background-color: var(--color-primary-container, #e8def8);
|
|
345
|
-
color: var(--color-on-primary-container, #1d1b20);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
.cascader-option.disabled {
|
|
349
|
-
opacity: 0.5;
|
|
350
|
-
cursor: not-allowed;
|
|
351
194
|
}
|
|
352
195
|
|
|
196
|
+
/* Checkbox (not in core) */
|
|
353
197
|
.cascader-option-checkbox {
|
|
354
198
|
display: flex;
|
|
355
199
|
align-items: center;
|
|
@@ -362,27 +206,13 @@ const styles = css`
|
|
|
362
206
|
flex-shrink: 0;
|
|
363
207
|
}
|
|
364
208
|
|
|
365
|
-
.cascader-option
|
|
209
|
+
.cascader-option-selected .cascader-option-checkbox {
|
|
366
210
|
background-color: var(--color-primary);
|
|
367
211
|
border-color: var(--color-primary);
|
|
368
212
|
color: var(--color-on-primary, white);
|
|
369
213
|
}
|
|
370
214
|
|
|
371
|
-
|
|
372
|
-
flex: 1;
|
|
373
|
-
overflow: hidden;
|
|
374
|
-
text-overflow: ellipsis;
|
|
375
|
-
white-space: nowrap;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.cascader-option-arrow {
|
|
379
|
-
display: flex;
|
|
380
|
-
align-items: center;
|
|
381
|
-
justify-content: center;
|
|
382
|
-
color: var(--color-on-surface-variant);
|
|
383
|
-
flex-shrink: 0;
|
|
384
|
-
}
|
|
385
|
-
|
|
215
|
+
/* Loading spinner (not in core) */
|
|
386
216
|
.cascader-option-loading {
|
|
387
217
|
display: flex;
|
|
388
218
|
align-items: center;
|
|
@@ -399,7 +229,7 @@ const styles = css`
|
|
|
399
229
|
to { transform: rotate(360deg); }
|
|
400
230
|
}
|
|
401
231
|
|
|
402
|
-
/* Search
|
|
232
|
+
/* Search results (not in core) */
|
|
403
233
|
.cascader-search-results {
|
|
404
234
|
padding: 0.25rem;
|
|
405
235
|
max-height: 18rem;
|
|
@@ -443,15 +273,7 @@ const styles = css`
|
|
|
443
273
|
margin: 0 0.25rem;
|
|
444
274
|
}
|
|
445
275
|
|
|
446
|
-
/*
|
|
447
|
-
.cascader-empty {
|
|
448
|
-
padding: 1.5rem;
|
|
449
|
-
text-align: center;
|
|
450
|
-
color: var(--color-on-surface-variant);
|
|
451
|
-
font-size: var(--font-size-sm, 0.875rem);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/* Size Variants */
|
|
276
|
+
/* Size variants via :host (override core's container-class approach) */
|
|
455
277
|
:host([size="sm"]) .cascader-trigger {
|
|
456
278
|
min-height: 2.25rem;
|
|
457
279
|
padding: 0.375rem 0.5rem;
|
|
@@ -466,7 +288,7 @@ const styles = css`
|
|
|
466
288
|
border-radius: var(--radius-lg, 0.625rem);
|
|
467
289
|
}
|
|
468
290
|
|
|
469
|
-
/* Validation
|
|
291
|
+
/* Validation states via :host (override core's container-class approach) */
|
|
470
292
|
:host([validation-state="invalid"]) .cascader-trigger {
|
|
471
293
|
border-color: var(--color-error);
|
|
472
294
|
}
|
|
@@ -480,7 +302,7 @@ const styles = css`
|
|
|
480
302
|
border-color: var(--color-success);
|
|
481
303
|
}
|
|
482
304
|
|
|
483
|
-
/* Disabled
|
|
305
|
+
/* Disabled state via :host */
|
|
484
306
|
:host([disabled]) {
|
|
485
307
|
pointer-events: none;
|
|
486
308
|
}
|
|
@@ -1047,7 +869,7 @@ export class ElDmCascader extends BaseElement {
|
|
|
1047
869
|
// Rendering
|
|
1048
870
|
protected render(): string {
|
|
1049
871
|
return `
|
|
1050
|
-
<div class="cascader ${this._isOpen ? 'open' : ''}">
|
|
872
|
+
<div class="cascader ${this._isOpen ? 'cascader-open' : ''}">
|
|
1051
873
|
${this._renderTrigger()}
|
|
1052
874
|
${this._renderDropdown()}
|
|
1053
875
|
</div>
|
|
@@ -1157,9 +979,9 @@ export class ElDmCascader extends BaseElement {
|
|
|
1157
979
|
|
|
1158
980
|
const classes = [
|
|
1159
981
|
'cascader-option',
|
|
1160
|
-
isActive ? 'active' : '',
|
|
1161
|
-
isSelected ? 'selected' : '',
|
|
1162
|
-
option.disabled ? 'disabled' : '',
|
|
982
|
+
isActive ? 'cascader-option-active' : '',
|
|
983
|
+
isSelected ? 'cascader-option-selected' : '',
|
|
984
|
+
option.disabled ? 'cascader-option-disabled' : '',
|
|
1163
985
|
].filter(Boolean).join(' ');
|
|
1164
986
|
|
|
1165
987
|
return `
|
|
@@ -1182,7 +1004,7 @@ export class ElDmCascader extends BaseElement {
|
|
|
1182
1004
|
|
|
1183
1005
|
return `
|
|
1184
1006
|
<div class="cascader-panel">
|
|
1185
|
-
<div class="cascader-
|
|
1007
|
+
<div class="cascader-options">${optionsHtml}</div>
|
|
1186
1008
|
</div>
|
|
1187
1009
|
`;
|
|
1188
1010
|
}
|