Skip to content

Commit 379fc7a

Browse files
authored
feat(select): defineExpose some methods & refs (#314)
* chore(cursor): add cursor rules * feat(select): defineExpose some methods & refs #313
1 parent 31f368c commit 379fc7a

File tree

8 files changed

+446
-0
lines changed

8 files changed

+446
-0
lines changed

.cursor/rules/comments.mdc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
alwaysApply: true
3+
---
4+
5+
# Scannable Code Guidelines/Separating Concerns.
6+
7+
## Core Principle
8+
9+
Code should be scannable through proper abstraction, not comments. Use variables and helper functions to make intent clear at a glance.
10+
11+
## Good Scannable Code
12+
13+
- Extract complex logic into well-named variables
14+
- Create helper functions for multi-step operations
15+
- Use descriptive names that explain intent
16+
17+
Examples:
18+
19+
```javascript
20+
// ✅ Scannable: Intent is clear from variable names
21+
const isValidUser = user.isActive && user.hasPermissions;
22+
const shouldProcessPayment = amount > 0 && !order.isPaid;
23+
24+
// ✅ Scannable: Complex logic extracted to helper
25+
const searchParams = buildFreesoundSearchParams({ query, filters, pagination });
26+
const transformedResults = transformFreesoundResults({ rawResults });
27+
```
28+
29+
## Bad Unscannable Code
30+
31+
Avoid:
32+
33+
```javascript
34+
// ❌ Hard to scan: What does this condition mean?
35+
if (type === "effects" || !type) {
36+
params.append("filter", "duration:[* TO 30.0]");
37+
params.append("filter", `avg_rating:[${min_rating} TO *]`);
38+
if (commercial_only) {
39+
params.append("filter", 'license:("Attribution" OR "Creative Commons 0")');
40+
}
41+
}
42+
43+
// ❌ Hard to scan: Complex ternary
44+
const sortParam = query
45+
? sort === "score"
46+
? "score"
47+
: `${sort}_desc`
48+
: `${sort}_desc`;
49+
```
50+
51+
## Rule
52+
53+
Make code scannable by extracting intent into variables and helper functions. If you need to think about what code does, extract it. The reader should understand the flow without diving into implementation details.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
alwaysApply: true
3+
description: End-to-end flow for implementing features and bug fixes using AI. Enforces playground demos, strict type-safety, tests, successful builds, and documentation updates.
4+
---
5+
6+
## Implementing Features & Fixes (AI Playbook)
7+
8+
Use this checklist for every feature or bug fix. Do not skip steps. Commands assume npm.
9+
10+
### 1. Write or update tests first
11+
- **Where**: colocate tests in `src/*.spec.ts` (see existing examples like `src/Select.spec.ts`).
12+
- **Run tests**: `npm run test`
13+
- Uses JSDOM and coverage settings from [vitest.config.ts](mdc:vitest.config.ts).
14+
- **Adapt existing tests** when changing behavior; add new tests for new props/events/slots.
15+
16+
### 2. Enforce type-safety (no ts-ignore)
17+
- **Run type-check**: `npm run type-check`
18+
- **Rule**: no `@ts-ignore` or `// @ts-expect-error`. Fix types instead.
19+
- **Types locations**: update when needed
20+
- Props: [src/types/props.ts](mdc:src/types/props.ts)
21+
- Options: [src/types/option.ts](mdc:src/types/option.ts)
22+
- Slots: [src/types/slots.ts](mdc:src/types/slots.ts)
23+
- Prefer extracting complex logic into helpers (see [src/lib/*](mdc:src/lib)) to keep components scannable.
24+
25+
### 3. Add a Playground demo (for new features)
26+
- **Create demo component** under `playground/demos/YourFeature.vue`.
27+
- **Register route** in [playground/main.ts](mdc:playground/main.ts)
28+
- Import your demo and add a `{ path: "/your-feature", component: YourFeature }` route.
29+
- **Add navigation link** in [playground/PlaygroundLayout.vue](mdc:playground/PlaygroundLayout.vue)
30+
- Append to `links`: `{ value: "/your-feature", label: "Your Feature" }`.
31+
- **Run playground**: `npm run dev:playground` and manually verify the UX.
32+
33+
### 4. Build must pass (no early exits)
34+
- **Run build**: `npm run build`
35+
- This runs `type-check` and then `vite build` via `build-only`.
36+
- Fix all errors; do not bypass checks. Do not introduce build-time warnings that indicate type holes.
37+
- Output is generated in `dist/` (do not edit `dist/` by hand).
38+
39+
### 5. Documentation (new features or behavior changes)
40+
- **Add a docs page** under `docs/demo/*.md` when showcasing a new capability (see existing demos in `docs/demo/`).
41+
- **Update reference docs** if applicable:
42+
- Getting started: [docs/getting-started.md](mdc:docs/getting-started.md)
43+
- Props: [docs/props.md](mdc:docs/props.md)
44+
- Events: [docs/events.md](mdc:docs/events.md)
45+
- Slots: [docs/slots.md](mdc:docs/slots.md)
46+
- Options: [docs/options.md](mdc:docs/options.md)
47+
- Styling: [docs/styling.md](mdc:docs/styling.md)
48+
- TypeScript: [docs/typescript.md](mdc:docs/typescript.md)
49+
- **Preview docs**: `npm run docs:dev` and ensure navigation from [docs/index.md](mdc:docs/index.md) remains coherent.
50+
- **Validate docs build**: `npm run docs:build`.
51+
52+
### 6. Linting
53+
- **Run**: `npm run lint`
54+
- **Auto-fix**: `npm run lint:fix` (then re-run `npm run lint`).
55+
56+
### 7. PR readiness checklist
57+
- Tests added/updated and `npm run test` passes.
58+
- `npm run type-check` passes with zero `@ts-ignore`.
59+
- New demo added in `playground/` (if new feature) and manually verified via `npm run dev:playground`.
60+
- `npm run build` completes without errors (no early exit).
61+
- Docs added/updated and `npm run docs:build` passes.
62+
- Code is scannable: complex logic extracted into helpers under `src/lib/` and meaningful names used.
63+
64+
### Useful references
65+
- Package scripts: [package.json](mdc:package.json)
66+
- Test config: [vitest.config.ts](mdc:vitest.config.ts)
67+
- Playground router: [playground/main.ts](mdc:playground/main.ts)
68+
- Playground layout links: [playground/PlaygroundLayout.vue](mdc:playground/PlaygroundLayout.vue)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
alwaysApply: true
3+
---
4+
5+
# Comment Guidelines
6+
7+
## Good Comments (Human-style)
8+
- Explain WHY, not WHAT
9+
- Document non-obvious behavior or edge cases
10+
- Warn about performance implications or side effects
11+
- Explain business logic that isn't clear from code
12+
13+
Examples:
14+
```javascript
15+
// transfer, not copy; sender buffer detaches
16+
// satisfies: check shape; keep literals
17+
// keep multibyte across chunks
18+
// timingSafeEqual throws on length mismatch
19+
```
20+
21+
## Bad Comments (AI-style)
22+
- Don't explain what the code literally does
23+
- Don't add changelog-style comments in code
24+
- Don't comment every line or obvious operations
25+
26+
Avoid:
27+
```javascript
28+
// Prevent duplicate initialization
29+
// Check if project is already loaded
30+
// Mark as initializing to prevent race conditions
31+
// (changed from blah to blah)
32+
```
33+
34+
## Rule
35+
Only add comments when there's genuinely non-obvious behavior, performance considerations, or business logic that needs context. Code should be self-documenting through naming and structure.

.cursor/rules/ultracite.mdc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
alwaysApply: true
3+
---
4+
5+
# Comment Guidelines
6+
7+
## Good Comments (Human-style)
8+
- Explain WHY, not WHAT
9+
- Document non-obvious behavior or edge cases
10+
- Warn about performance implications or side effects
11+
- Explain business logic that isn't clear from code
12+
13+
Examples:
14+
```javascript
15+
// transfer, not copy; sender buffer detaches
16+
// satisfies: check shape; keep literals
17+
// keep multibyte across chunks
18+
// timingSafeEqual throws on length mismatch
19+
```
20+
21+
## Bad Comments (AI-style)
22+
- Don't explain what the code literally does
23+
- Don't add changelog-style comments in code
24+
- Don't comment every line or obvious operations
25+
26+
Avoid:
27+
```javascript
28+
// Prevent duplicate initialization
29+
// Check if project is already loaded
30+
// Mark as initializing to prevent race conditions
31+
// (changed from blah to blah)
32+
```
33+
34+
## Rule
35+
Only add comments when there's genuinely non-obvious behavior, performance considerations, or business logic that needs context. Code should be self-documenting through naming and structure.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
alwaysApply: true
3+
---
4+
5+
# Scannable Code Guidelines/Separating Concerns.
6+
7+
## Core Principle
8+
9+
Code should be scannable through proper abstraction, not comments. Use variables and helper functions to make intent clear at a glance.
10+
11+
## Good Scannable Code
12+
13+
- Extract complex logic into well-named variables
14+
- Create helper functions for multi-step operations
15+
- Use descriptive names that explain intent
16+
17+
Examples:
18+
19+
```javascript
20+
// ✅ Scannable: Intent is clear from variable names
21+
const isValidUser = user.isActive && user.hasPermissions;
22+
const shouldProcessPayment = amount > 0 && !order.isPaid;
23+
24+
// ✅ Scannable: Complex logic extracted to helper
25+
const searchParams = buildFreesoundSearchParams({ query, filters, pagination });
26+
const transformedResults = transformFreesoundResults({ rawResults });
27+
```
28+
29+
## Bad Unscannable Code
30+
31+
Avoid:
32+
33+
```javascript
34+
// ❌ Hard to scan: What does this condition mean?
35+
if (type === "effects" || !type) {
36+
params.append("filter", "duration:[* TO 30.0]");
37+
params.append("filter", `avg_rating:[${min_rating} TO *]`);
38+
if (commercial_only) {
39+
params.append("filter", 'license:("Attribution" OR "Creative Commons 0")');
40+
}
41+
}
42+
43+
// ❌ Hard to scan: Complex ternary
44+
const sortParam = query
45+
? sort === "score"
46+
? "score"
47+
: `${sort}_desc`
48+
: `${sort}_desc`;
49+
```
50+
51+
## Rule
52+
53+
Make code scannable by extracting intent into variables and helper functions. If you need to think about what code does, extract it. The reader should understand the flow without diving into implementation details.

docs/typescript.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,114 @@ const roleOptions = [
165165
/>
166166
</template>
167167
```
168+
169+
## Component ref and exposed API
170+
171+
The Vue3 Select component exposes several refs and methods through `defineExpose()` for programmatic control. This provides a type-safe way to interact with the component instance.
172+
173+
### Available exposed properties
174+
175+
```ts
176+
type ExposedAPI = {
177+
// DOM element refs
178+
inputRef: Ref<HTMLInputElement | undefined>;
179+
containerRef: Ref<HTMLDivElement | undefined>;
180+
181+
// Control methods
182+
openMenu: () => void;
183+
closeMenu: () => void;
184+
toggleMenu: () => void;
185+
clear: () => void;
186+
};
187+
```
188+
189+
### Accessing the component ref
190+
191+
Use template refs to access the exposed API:
192+
193+
```vue
194+
<script setup lang="ts">
195+
import { nextTick, ref } from "vue";
196+
import VueSelect from "vue3-select-component";
197+
198+
const selectRef = ref<InstanceType<typeof VueSelect>>();
199+
const selectedValue = ref<string | null>(null);
200+
201+
const options = [
202+
{ label: "Option 1", value: "1" },
203+
{ label: "Option 2", value: "2" },
204+
];
205+
206+
// Focus the input programmatically
207+
const focusSelect = async () => {
208+
await nextTick();
209+
selectRef.value?.inputRef?.focus();
210+
};
211+
212+
// Open the dropdown menu
213+
const openDropdown = () => {
214+
selectRef.value?.openMenu();
215+
};
216+
217+
// Clear the selection
218+
const clearSelection = () => {
219+
selectRef.value?.clear();
220+
};
221+
</script>
222+
223+
<template>
224+
<VueSelect
225+
ref="selectRef"
226+
v-model="selectedValue"
227+
:options="options"
228+
placeholder="Select an option"
229+
/>
230+
231+
<button @click="focusSelect">
232+
Focus Input
233+
</button>
234+
<button @click="openDropdown">
235+
Open Menu
236+
</button>
237+
<button @click="clearSelection">
238+
Clear
239+
</button>
240+
</template>
241+
```
242+
243+
### Type safety with multi-select
244+
245+
When using multi-select, ensure proper typing:
246+
247+
```vue
248+
<script setup lang="ts">
249+
import type { Option } from "vue3-select-component";
250+
import { ref } from "vue";
251+
import VueSelect from "vue3-select-component";
252+
253+
type UserOption = Option<number>;
254+
255+
const selectRef = ref<InstanceType<typeof VueSelect<UserOption, number>>>();
256+
const selectedUsers = ref<number[]>([]);
257+
258+
const userOptions: UserOption[] = [
259+
{ label: "Alice", value: 1 },
260+
{ label: "Bob", value: 2 },
261+
];
262+
</script>
263+
264+
<template>
265+
<VueSelect
266+
ref="selectRef"
267+
v-model="selectedUsers"
268+
:options="userOptions"
269+
:is-multi="true"
270+
/>
271+
</template>
272+
```
273+
274+
### Best practices
275+
276+
- Always use `nextTick()` when accessing DOM elements after component mounting
277+
- Check for element existence before calling methods: `selectRef.value?.inputRef?.focus()`
278+
- Type the ref with `InstanceType<typeof VueSelect>` for full TypeScript support

0 commit comments

Comments
 (0)