@ssejal8/recipe-ui-kit 0.1.0 → 0.1.1
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 +108 -277
- package/dist/cjs/index-BEr8BsSA.js +1712 -0
- package/dist/cjs/index-BEr8BsSA.js.map +1 -0
- package/dist/cjs/index.cjs.js +5 -0
- package/dist/cjs/index.cjs.js.map +1 -0
- package/dist/cjs/loader.cjs.js +15 -0
- package/dist/cjs/loader.cjs.js.map +1 -0
- package/dist/cjs/recipe-kit.cjs.js +27 -0
- package/dist/cjs/recipe-kit.cjs.js.map +1 -0
- package/dist/cjs/rk-badge_10.cjs.entry.js +638 -0
- package/dist/cjs/rk-badge_10.cjs.entry.js.map +1 -0
- package/dist/collection/collection-manifest.json +22 -0
- package/dist/collection/components/rk-badge/rk-badge.js +97 -0
- package/dist/collection/components/rk-badge/rk-badge.js.map +1 -0
- package/dist/collection/components/rk-chip/rk-chip.js +245 -0
- package/dist/collection/components/rk-chip/rk-chip.js.map +1 -0
- package/dist/collection/components/rk-empty-state/rk-empty-state.js +138 -0
- package/dist/collection/components/rk-empty-state/rk-empty-state.js.map +1 -0
- package/dist/collection/components/rk-meal-slot/rk-meal-slot.js +312 -0
- package/dist/collection/components/rk-meal-slot/rk-meal-slot.js.map +1 -0
- package/dist/collection/components/rk-modal/rk-modal.js +256 -0
- package/dist/collection/components/rk-modal/rk-modal.js.map +1 -0
- package/dist/collection/components/rk-rating/rk-rating.js +228 -0
- package/dist/collection/components/rk-rating/rk-rating.js.map +1 -0
- package/dist/collection/components/rk-recipe-card/rk-recipe-card.js +366 -0
- package/dist/collection/components/rk-recipe-card/rk-recipe-card.js.map +1 -0
- package/dist/collection/components/rk-search-bar/rk-search-bar.js +291 -0
- package/dist/collection/components/rk-search-bar/rk-search-bar.js.map +1 -0
- package/dist/collection/components/rk-segmented-control/rk-segmented-control.js +196 -0
- package/dist/collection/components/rk-segmented-control/rk-segmented-control.js.map +1 -0
- package/dist/collection/components/rk-tag-input/rk-tag-input.js +264 -0
- package/dist/collection/components/rk-tag-input/rk-tag-input.js.map +1 -0
- package/dist/collection/index.js +2 -0
- package/dist/collection/index.js.map +1 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/rk-badge.d.ts +11 -0
- package/dist/components/rk-badge.js +2 -0
- package/dist/components/rk-badge.js.map +1 -0
- package/dist/components/rk-chip.d.ts +11 -0
- package/dist/components/rk-chip.js +2 -0
- package/dist/components/rk-chip.js.map +1 -0
- package/dist/components/rk-empty-state.d.ts +11 -0
- package/dist/components/rk-empty-state.js +2 -0
- package/dist/components/rk-empty-state.js.map +1 -0
- package/dist/components/rk-meal-slot.d.ts +11 -0
- package/dist/components/rk-meal-slot.js +2 -0
- package/dist/components/rk-meal-slot.js.map +1 -0
- package/dist/components/rk-modal.d.ts +11 -0
- package/dist/components/rk-modal.js +2 -0
- package/dist/components/rk-modal.js.map +1 -0
- package/dist/components/rk-rating.d.ts +11 -0
- package/dist/components/rk-rating.js +2 -0
- package/dist/components/rk-rating.js.map +1 -0
- package/dist/components/rk-recipe-card.d.ts +11 -0
- package/dist/components/rk-recipe-card.js +2 -0
- package/dist/components/rk-recipe-card.js.map +1 -0
- package/dist/components/rk-search-bar.d.ts +11 -0
- package/dist/components/rk-search-bar.js +2 -0
- package/dist/components/rk-search-bar.js.map +1 -0
- package/dist/components/rk-segmented-control.d.ts +11 -0
- package/dist/components/rk-segmented-control.js +2 -0
- package/dist/components/rk-segmented-control.js.map +1 -0
- package/dist/components/rk-tag-input.d.ts +11 -0
- package/dist/components/rk-tag-input.js +2 -0
- package/dist/components/rk-tag-input.js.map +1 -0
- package/dist/esm/index-Bxo0C8lB.js +1684 -0
- package/dist/esm/index-Bxo0C8lB.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/loader.js +13 -0
- package/dist/esm/loader.js.map +1 -0
- package/dist/esm/recipe-kit.js +23 -0
- package/dist/esm/recipe-kit.js.map +1 -0
- package/dist/esm/rk-badge_10.entry.js +627 -0
- package/dist/esm/rk-badge_10.entry.js.map +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/recipe-kit/index.esm.js +2 -0
- package/dist/recipe-kit/index.esm.js.map +1 -0
- package/dist/recipe-kit/p-Bxo0C8lB.js +3 -0
- package/dist/recipe-kit/p-Bxo0C8lB.js.map +1 -0
- package/dist/recipe-kit/p-fcb955c7.entry.js +2 -0
- package/dist/recipe-kit/p-fcb955c7.entry.js.map +1 -0
- package/dist/recipe-kit/recipe-kit.css +1 -0
- package/dist/recipe-kit/recipe-kit.esm.js +2 -0
- package/dist/recipe-kit/recipe-kit.esm.js.map +1 -0
- package/dist/recipe-kit/rk-global.css +104 -0
- package/dist/types/components/rk-badge/rk-badge.d.ts +15 -0
- package/dist/types/components/rk-chip/rk-chip.d.ts +37 -0
- package/dist/types/components/rk-empty-state/rk-empty-state.d.ts +19 -0
- package/dist/types/components/rk-meal-slot/rk-meal-slot.d.ts +53 -0
- package/dist/types/components/rk-modal/rk-modal.d.ts +41 -0
- package/dist/types/components/rk-rating/rk-rating.d.ts +26 -0
- package/dist/types/components/rk-recipe-card/rk-recipe-card.d.ts +67 -0
- package/dist/types/components/rk-search-bar/rk-search-bar.d.ts +39 -0
- package/dist/types/components/rk-segmented-control/rk-segmented-control.d.ts +37 -0
- package/dist/types/components/rk-tag-input/rk-tag-input.d.ts +35 -0
- package/{packages/recipe-kit-stencil/src → dist/types}/components.d.ts +1 -1
- package/{packages/recipe-kit-stencil/src/index.ts → dist/types/index.d.ts} +0 -2
- package/dist/types/stencil-public-runtime.d.ts +1873 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +84 -22
- package/.claude/settings.local.json +0 -10
- package/.npmr +0 -1
- package/apps/recipe-web/package.json +0 -31
- package/apps/recipe-web/src/app.css +0 -418
- package/apps/recipe-web/src/app.d.ts +0 -32
- package/apps/recipe-web/src/app.html +0 -25
- package/apps/recipe-web/src/lib/api/mealdb.ts +0 -240
- package/apps/recipe-web/src/lib/components/AppHeader.svelte +0 -290
- package/apps/recipe-web/src/lib/components/ConfirmDialog.svelte +0 -46
- package/apps/recipe-web/src/lib/components/Modal.svelte +0 -33
- package/apps/recipe-web/src/lib/components/RecipeCard.svelte +0 -100
- package/apps/recipe-web/src/lib/components/RecipeForm.svelte +0 -560
- package/apps/recipe-web/src/lib/components/RecipeGrid.svelte +0 -77
- package/apps/recipe-web/src/lib/components/RecipePickerDialog.svelte +0 -185
- package/apps/recipe-web/src/lib/components/Toaster.svelte +0 -111
- package/apps/recipe-web/src/lib/domain/recipe.ts +0 -130
- package/apps/recipe-web/src/lib/domain/validation.ts +0 -189
- package/apps/recipe-web/src/lib/stencil/WebComponent.svelte +0 -145
- package/apps/recipe-web/src/lib/stencil/register.ts +0 -28
- package/apps/recipe-web/src/lib/stores/favorites.svelte.ts +0 -81
- package/apps/recipe-web/src/lib/stores/myRecipes.svelte.ts +0 -98
- package/apps/recipe-web/src/lib/stores/persisted.svelte.ts +0 -73
- package/apps/recipe-web/src/lib/stores/planner.svelte.ts +0 -275
- package/apps/recipe-web/src/lib/stores/theme.svelte.ts +0 -67
- package/apps/recipe-web/src/lib/stores/toast.svelte.ts +0 -66
- package/apps/recipe-web/src/routes/+error.svelte +0 -36
- package/apps/recipe-web/src/routes/+layout.svelte +0 -62
- package/apps/recipe-web/src/routes/+page.svelte +0 -352
- package/apps/recipe-web/src/routes/+page.ts +0 -107
- package/apps/recipe-web/src/routes/favorites/+page.svelte +0 -170
- package/apps/recipe-web/src/routes/my-recipes/+layout.ts +0 -55
- package/apps/recipe-web/src/routes/my-recipes/+page.svelte +0 -180
- package/apps/recipe-web/src/routes/my-recipes/[id]/edit/+page.svelte +0 -104
- package/apps/recipe-web/src/routes/my-recipes/new/+page.svelte +0 -67
- package/apps/recipe-web/src/routes/planner/+page.svelte +0 -422
- package/apps/recipe-web/src/routes/recipes/[id]/+page.svelte +0 -538
- package/apps/recipe-web/src/routes/recipes/[id]/+page.ts +0 -24
- package/apps/recipe-web/static/favicon.svg +0 -8
- package/apps/recipe-web/static/robots.txt +0 -2
- package/apps/recipe-web/svelte.config.js +0 -35
- package/apps/recipe-web/tsconfig.json +0 -14
- package/apps/recipe-web/vite.config.ts +0 -23
- package/packages/recipe-kit-stencil/README.md +0 -185
- package/packages/recipe-kit-stencil/package-lock.json +0 -4772
- package/packages/recipe-kit-stencil/package.json +0 -91
- package/packages/recipe-kit-stencil/src/components/rk-badge/readme.md +0 -38
- package/packages/recipe-kit-stencil/src/components/rk-badge/rk-badge.tsx +0 -31
- package/packages/recipe-kit-stencil/src/components/rk-chip/readme.md +0 -52
- package/packages/recipe-kit-stencil/src/components/rk-chip/rk-chip.tsx +0 -105
- package/packages/recipe-kit-stencil/src/components/rk-empty-state/readme.md +0 -44
- package/packages/recipe-kit-stencil/src/components/rk-empty-state/rk-empty-state.tsx +0 -50
- package/packages/recipe-kit-stencil/src/components/rk-meal-slot/readme.md +0 -58
- package/packages/recipe-kit-stencil/src/components/rk-meal-slot/rk-meal-slot.tsx +0 -182
- package/packages/recipe-kit-stencil/src/components/rk-modal/readme.md +0 -57
- package/packages/recipe-kit-stencil/src/components/rk-modal/rk-modal.tsx +0 -176
- package/packages/recipe-kit-stencil/src/components/rk-rating/readme.md +0 -42
- package/packages/recipe-kit-stencil/src/components/rk-rating/rk-rating.tsx +0 -114
- package/packages/recipe-kit-stencil/src/components/rk-recipe-card/readme.md +0 -66
- package/packages/recipe-kit-stencil/src/components/rk-recipe-card/rk-recipe-card.tsx +0 -179
- package/packages/recipe-kit-stencil/src/components/rk-recipe-card/test/rk-recipe-card.spec.tsx +0 -70
- package/packages/recipe-kit-stencil/src/components/rk-search-bar/readme.md +0 -67
- package/packages/recipe-kit-stencil/src/components/rk-search-bar/rk-search-bar.tsx +0 -152
- package/packages/recipe-kit-stencil/src/components/rk-search-bar/test/rk-search-bar.spec.tsx +0 -93
- package/packages/recipe-kit-stencil/src/components/rk-segmented-control/readme.md +0 -41
- package/packages/recipe-kit-stencil/src/components/rk-segmented-control/rk-segmented-control.tsx +0 -115
- package/packages/recipe-kit-stencil/src/components/rk-tag-input/readme.md +0 -47
- package/packages/recipe-kit-stencil/src/components/rk-tag-input/rk-tag-input.tsx +0 -181
- package/packages/recipe-kit-stencil/src/components/rk-tag-input/test/rk-tag-input.spec.tsx +0 -76
- package/packages/recipe-kit-stencil/src/index.html +0 -259
- package/packages/recipe-kit-stencil/stencil.config.ts +0 -41
- package/packages/recipe-kit-stencil/tsconfig.json +0 -24
- /package/{packages/recipe-kit-stencil/LICENSE → LICENSE} +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-badge/rk-badge.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-chip/rk-chip.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-empty-state/rk-empty-state.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-meal-slot/rk-meal-slot.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-modal/rk-modal.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-rating/rk-rating.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-recipe-card/rk-recipe-card.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-search-bar/rk-search-bar.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-segmented-control/rk-segmented-control.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-tag-input/rk-tag-input.css +0 -0
- /package/{packages/recipe-kit-stencil/src/global → dist/collection}/rk-global.css +0 -0
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { Component, Event, EventEmitter, h, Host, Prop, State, Watch } from '@stencil/core';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A tag / token editor used for recipe tags and free-form ingredient lists.
|
|
5
|
-
*
|
|
6
|
-
* `values` is an array property — the SvelteKit app assigns it as a DOM
|
|
7
|
-
* property. A JSON-encoded or comma-separated string attribute also works.
|
|
8
|
-
*/
|
|
9
|
-
@Component({
|
|
10
|
-
tag: 'rk-tag-input',
|
|
11
|
-
styleUrl: 'rk-tag-input.css',
|
|
12
|
-
shadow: true,
|
|
13
|
-
})
|
|
14
|
-
export class RkTagInput {
|
|
15
|
-
/** Current tags. Array, JSON string, or comma-separated string. */
|
|
16
|
-
@Prop() values: string[] | string = [];
|
|
17
|
-
|
|
18
|
-
/** Placeholder for the entry field. */
|
|
19
|
-
@Prop() placeholder = 'Add a tag and press Enter';
|
|
20
|
-
|
|
21
|
-
/** Visible field label. */
|
|
22
|
-
@Prop() label?: string;
|
|
23
|
-
|
|
24
|
-
/** Maximum number of tags. `0` means unlimited. */
|
|
25
|
-
@Prop() maxTags = 0;
|
|
26
|
-
|
|
27
|
-
/** Prevent editing. */
|
|
28
|
-
@Prop() disabled = false;
|
|
29
|
-
|
|
30
|
-
/** Validation message rendered under the field. */
|
|
31
|
-
@Prop() error?: string;
|
|
32
|
-
|
|
33
|
-
/** Emitted with the full, updated list whenever tags change. */
|
|
34
|
-
@Event({ eventName: 'rkTagsChange' }) rkTagsChange!: EventEmitter<string[]>;
|
|
35
|
-
|
|
36
|
-
@State() private draft = '';
|
|
37
|
-
@State() private tags: string[] = [];
|
|
38
|
-
|
|
39
|
-
private inputEl?: HTMLInputElement;
|
|
40
|
-
|
|
41
|
-
@Watch('values')
|
|
42
|
-
protected onValuesChange() {
|
|
43
|
-
this.syncTags();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
componentWillLoad() {
|
|
47
|
-
this.syncTags();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private syncTags() {
|
|
51
|
-
const incoming = this.values;
|
|
52
|
-
if (Array.isArray(incoming)) {
|
|
53
|
-
this.tags = incoming.filter(tag => typeof tag === 'string');
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (typeof incoming === 'string' && incoming.trim()) {
|
|
57
|
-
const trimmed = incoming.trim();
|
|
58
|
-
if (trimmed.startsWith('[')) {
|
|
59
|
-
try {
|
|
60
|
-
const decoded = JSON.parse(trimmed);
|
|
61
|
-
this.tags = Array.isArray(decoded) ? decoded.map(String) : [];
|
|
62
|
-
return;
|
|
63
|
-
} catch {
|
|
64
|
-
/* fall through to comma parsing */
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
this.tags = trimmed
|
|
68
|
-
.split(',')
|
|
69
|
-
.map(tag => tag.trim())
|
|
70
|
-
.filter(Boolean);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
this.tags = [];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private get atCapacity() {
|
|
77
|
-
return this.maxTags > 0 && this.tags.length >= this.maxTags;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private commit(next: string[]) {
|
|
81
|
-
this.tags = next;
|
|
82
|
-
this.rkTagsChange.emit([...next]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private addDraft() {
|
|
86
|
-
const candidates = this.draft
|
|
87
|
-
.split(',')
|
|
88
|
-
.map(tag => tag.trim())
|
|
89
|
-
.filter(Boolean);
|
|
90
|
-
if (candidates.length === 0) return;
|
|
91
|
-
|
|
92
|
-
const next = [...this.tags];
|
|
93
|
-
for (const candidate of candidates) {
|
|
94
|
-
if (this.maxTags > 0 && next.length >= this.maxTags) break;
|
|
95
|
-
// Case-insensitive de-duplication keeps "Vegan" and "vegan" from coexisting.
|
|
96
|
-
if (next.some(tag => tag.toLowerCase() === candidate.toLowerCase())) continue;
|
|
97
|
-
next.push(candidate);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this.draft = '';
|
|
101
|
-
if (next.length !== this.tags.length) this.commit(next);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private removeAt(index: number) {
|
|
105
|
-
this.commit(this.tags.filter((_, position) => position !== index));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private handleKeyDown = (event: KeyboardEvent) => {
|
|
109
|
-
if (event.key === 'Enter' || event.key === ',') {
|
|
110
|
-
event.preventDefault();
|
|
111
|
-
this.addDraft();
|
|
112
|
-
} else if (event.key === 'Backspace' && this.draft === '' && this.tags.length > 0) {
|
|
113
|
-
event.preventDefault();
|
|
114
|
-
this.removeAt(this.tags.length - 1);
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
render() {
|
|
119
|
-
return (
|
|
120
|
-
<Host>
|
|
121
|
-
{this.label && (
|
|
122
|
-
<label class="label" part="label" onClick={() => this.inputEl?.focus()}>
|
|
123
|
-
{this.label}
|
|
124
|
-
{this.maxTags > 0 && (
|
|
125
|
-
<span class="counter">
|
|
126
|
-
{this.tags.length}/{this.maxTags}
|
|
127
|
-
</span>
|
|
128
|
-
)}
|
|
129
|
-
</label>
|
|
130
|
-
)}
|
|
131
|
-
|
|
132
|
-
<div
|
|
133
|
-
class={{ shell: true, 'is-disabled': this.disabled, 'has-error': !!this.error }}
|
|
134
|
-
part="shell"
|
|
135
|
-
onClick={() => !this.disabled && this.inputEl?.focus()}
|
|
136
|
-
>
|
|
137
|
-
{this.tags.map((tag, index) => (
|
|
138
|
-
<span class="tag" part="tag" key={`${tag}-${index}`}>
|
|
139
|
-
<span class="tag-text">{tag}</span>
|
|
140
|
-
<button
|
|
141
|
-
class="tag-remove"
|
|
142
|
-
type="button"
|
|
143
|
-
aria-label={`Remove ${tag}`}
|
|
144
|
-
disabled={this.disabled}
|
|
145
|
-
onClick={event => {
|
|
146
|
-
event.stopPropagation();
|
|
147
|
-
this.removeAt(index);
|
|
148
|
-
}}
|
|
149
|
-
>
|
|
150
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true">
|
|
151
|
-
<path d="M6 6l12 12M18 6 6 18" />
|
|
152
|
-
</svg>
|
|
153
|
-
</button>
|
|
154
|
-
</span>
|
|
155
|
-
))}
|
|
156
|
-
|
|
157
|
-
<input
|
|
158
|
-
ref={el => (this.inputEl = el)}
|
|
159
|
-
class="entry"
|
|
160
|
-
part="input"
|
|
161
|
-
type="text"
|
|
162
|
-
value={this.draft}
|
|
163
|
-
placeholder={this.tags.length === 0 ? this.placeholder : this.atCapacity ? '' : 'Add another…'}
|
|
164
|
-
aria-label={this.label ?? 'Add a tag'}
|
|
165
|
-
disabled={this.disabled || this.atCapacity}
|
|
166
|
-
autocomplete="off"
|
|
167
|
-
onInput={event => (this.draft = (event.target as HTMLInputElement).value)}
|
|
168
|
-
onKeyDown={this.handleKeyDown}
|
|
169
|
-
onBlur={() => this.addDraft()}
|
|
170
|
-
/>
|
|
171
|
-
</div>
|
|
172
|
-
|
|
173
|
-
{this.error && (
|
|
174
|
-
<p class="error" part="error" role="alert">
|
|
175
|
-
{this.error}
|
|
176
|
-
</p>
|
|
177
|
-
)}
|
|
178
|
-
</Host>
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { newSpecPage } from '@stencil/core/testing';
|
|
2
|
-
import { RkTagInput } from '../rk-tag-input';
|
|
3
|
-
|
|
4
|
-
describe('rk-tag-input', () => {
|
|
5
|
-
async function setup(html = `<rk-tag-input></rk-tag-input>`) {
|
|
6
|
-
const page = await newSpecPage({ components: [RkTagInput], html });
|
|
7
|
-
const input = page.root.shadowRoot.querySelector('input') as HTMLInputElement;
|
|
8
|
-
const onChange = jest.fn();
|
|
9
|
-
page.root.addEventListener('rkTagsChange', onChange);
|
|
10
|
-
return { page, input, onChange };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function enter(page: Awaited<ReturnType<typeof newSpecPage>>, input: HTMLInputElement, value: string) {
|
|
14
|
-
input.value = value;
|
|
15
|
-
input.dispatchEvent(new Event('input'));
|
|
16
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
|
17
|
-
await page.waitForChanges();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
it('adds a tag on Enter and emits the whole list', async () => {
|
|
21
|
-
const { page, input, onChange } = await setup();
|
|
22
|
-
|
|
23
|
-
await enter(page, input, 'vegan');
|
|
24
|
-
expect(onChange.mock.calls[0][0].detail).toEqual(['vegan']);
|
|
25
|
-
|
|
26
|
-
await enter(page, input, 'one pot');
|
|
27
|
-
expect(onChange.mock.calls[1][0].detail).toEqual(['vegan', 'one pot']);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('splits a comma-separated entry into several tags', async () => {
|
|
31
|
-
const { page, input, onChange } = await setup();
|
|
32
|
-
|
|
33
|
-
await enter(page, input, 'quick, easy, spicy');
|
|
34
|
-
expect(onChange.mock.calls[0][0].detail).toEqual(['quick', 'easy', 'spicy']);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('rejects case-insensitive duplicates', async () => {
|
|
38
|
-
const { page, input, onChange } = await setup();
|
|
39
|
-
|
|
40
|
-
await enter(page, input, 'Vegan');
|
|
41
|
-
await enter(page, input, 'vegan');
|
|
42
|
-
|
|
43
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
44
|
-
expect(page.root.shadowRoot.querySelectorAll('.tag')).toHaveLength(1);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('stops accepting tags at maxTags', async () => {
|
|
48
|
-
const { page, input, onChange } = await setup(`<rk-tag-input max-tags="2"></rk-tag-input>`);
|
|
49
|
-
|
|
50
|
-
await enter(page, input, 'a');
|
|
51
|
-
await enter(page, input, 'b');
|
|
52
|
-
expect(onChange).toHaveBeenCalledTimes(2);
|
|
53
|
-
|
|
54
|
-
expect(input.disabled).toBe(true);
|
|
55
|
-
expect(page.root.shadowRoot.querySelectorAll('.tag')).toHaveLength(2);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('accepts a JSON string attribute as a fallback for the array property', async () => {
|
|
59
|
-
const page = await newSpecPage({
|
|
60
|
-
components: [RkTagInput],
|
|
61
|
-
html: `<rk-tag-input values='["comfort food","one pot"]'></rk-tag-input>`,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const labels = Array.from(page.root.shadowRoot.querySelectorAll('.tag-text')).map(node => node.textContent);
|
|
65
|
-
expect(labels).toEqual(['comfort food', 'one pot']);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('removes the last tag on Backspace when the field is empty', async () => {
|
|
69
|
-
const { page, input, onChange } = await setup(`<rk-tag-input values='["a","b"]'></rk-tag-input>`);
|
|
70
|
-
|
|
71
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }));
|
|
72
|
-
await page.waitForChanges();
|
|
73
|
-
|
|
74
|
-
expect(onChange.mock.calls[0][0].detail).toEqual(['a']);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en" dir="ltr">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>recipe-kit-stencil — component gallery</title>
|
|
7
|
-
<script type="module" src="/build/recipe-kit.esm.js"></script>
|
|
8
|
-
<script nomodule src="/build/recipe-kit.js"></script>
|
|
9
|
-
<style>
|
|
10
|
-
body {
|
|
11
|
-
margin: 0;
|
|
12
|
-
padding: 40px 24px 96px;
|
|
13
|
-
background: var(--rk-bg);
|
|
14
|
-
color: var(--rk-ink);
|
|
15
|
-
font-family: var(--rk-font);
|
|
16
|
-
font-size: 15px;
|
|
17
|
-
}
|
|
18
|
-
main {
|
|
19
|
-
max-width: 960px;
|
|
20
|
-
margin: 0 auto;
|
|
21
|
-
display: grid;
|
|
22
|
-
gap: 44px;
|
|
23
|
-
}
|
|
24
|
-
h1 {
|
|
25
|
-
font-family: var(--rk-font-display);
|
|
26
|
-
font-size: 2.1rem;
|
|
27
|
-
margin: 0 0 6px;
|
|
28
|
-
letter-spacing: -0.02em;
|
|
29
|
-
}
|
|
30
|
-
.lede {
|
|
31
|
-
color: var(--rk-muted);
|
|
32
|
-
margin: 0;
|
|
33
|
-
max-width: 60ch;
|
|
34
|
-
line-height: 1.6;
|
|
35
|
-
}
|
|
36
|
-
section h2 {
|
|
37
|
-
font-size: 0.78rem;
|
|
38
|
-
text-transform: uppercase;
|
|
39
|
-
letter-spacing: 0.09em;
|
|
40
|
-
color: var(--rk-muted);
|
|
41
|
-
margin: 0 0 14px;
|
|
42
|
-
padding-bottom: 8px;
|
|
43
|
-
border-bottom: 1px solid var(--rk-border);
|
|
44
|
-
}
|
|
45
|
-
.row {
|
|
46
|
-
display: flex;
|
|
47
|
-
flex-wrap: wrap;
|
|
48
|
-
gap: 12px;
|
|
49
|
-
align-items: center;
|
|
50
|
-
}
|
|
51
|
-
.grid {
|
|
52
|
-
display: grid;
|
|
53
|
-
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
|
|
54
|
-
gap: 18px;
|
|
55
|
-
}
|
|
56
|
-
.planner {
|
|
57
|
-
display: grid;
|
|
58
|
-
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
59
|
-
gap: 12px;
|
|
60
|
-
}
|
|
61
|
-
button.demo {
|
|
62
|
-
font: inherit;
|
|
63
|
-
font-weight: 600;
|
|
64
|
-
padding: 9px 16px;
|
|
65
|
-
border-radius: 999px;
|
|
66
|
-
border: 1px solid var(--rk-border);
|
|
67
|
-
background: var(--rk-surface);
|
|
68
|
-
color: var(--rk-ink);
|
|
69
|
-
cursor: pointer;
|
|
70
|
-
}
|
|
71
|
-
#log {
|
|
72
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
73
|
-
font-size: 12px;
|
|
74
|
-
background: var(--rk-ink);
|
|
75
|
-
color: #d8f5c8;
|
|
76
|
-
padding: 14px 16px;
|
|
77
|
-
border-radius: 12px;
|
|
78
|
-
max-height: 180px;
|
|
79
|
-
overflow: auto;
|
|
80
|
-
white-space: pre-wrap;
|
|
81
|
-
margin: 0;
|
|
82
|
-
}
|
|
83
|
-
</style>
|
|
84
|
-
</head>
|
|
85
|
-
<body>
|
|
86
|
-
<main>
|
|
87
|
-
<header>
|
|
88
|
-
<h1>recipe-kit-stencil</h1>
|
|
89
|
-
<p class="lede">
|
|
90
|
-
A framework-agnostic web component library built with StencilJS. Every event below is logged at the bottom of the page — the same events the
|
|
91
|
-
SvelteKit application listens for.
|
|
92
|
-
</p>
|
|
93
|
-
</header>
|
|
94
|
-
|
|
95
|
-
<section>
|
|
96
|
-
<h2>rk-search-bar</h2>
|
|
97
|
-
<rk-search-bar hint="Debounced at 350 ms. Press Escape to clear.">
|
|
98
|
-
<rk-badge slot="actions" tone="accent">beta</rk-badge>
|
|
99
|
-
</rk-search-bar>
|
|
100
|
-
</section>
|
|
101
|
-
|
|
102
|
-
<section>
|
|
103
|
-
<h2>rk-chip & rk-segmented-control</h2>
|
|
104
|
-
<div class="row">
|
|
105
|
-
<rk-chip label="Seafood" value="Seafood" count="18"><span>🐟</span></rk-chip>
|
|
106
|
-
<rk-chip label="Dessert" value="Dessert" selected tone="primary"></rk-chip>
|
|
107
|
-
<rk-chip label="Vegan" value="Vegan" tone="accent" selected></rk-chip>
|
|
108
|
-
<rk-chip label="Italian" value="Italian" dismissible></rk-chip>
|
|
109
|
-
</div>
|
|
110
|
-
<br />
|
|
111
|
-
<rk-segmented-control id="segments" label="Sort recipes"></rk-segmented-control>
|
|
112
|
-
</section>
|
|
113
|
-
|
|
114
|
-
<section>
|
|
115
|
-
<h2>rk-badge & rk-rating</h2>
|
|
116
|
-
<div class="row">
|
|
117
|
-
<rk-badge>neutral</rk-badge>
|
|
118
|
-
<rk-badge tone="primary" variant="solid">primary solid</rk-badge>
|
|
119
|
-
<rk-badge tone="accent" variant="outline">accent outline</rk-badge>
|
|
120
|
-
<rk-badge tone="success" small>30 min</rk-badge>
|
|
121
|
-
<rk-rating value="4" show-value count="128"></rk-rating>
|
|
122
|
-
<rk-rating value="3.5" readonly show-value></rk-rating>
|
|
123
|
-
</div>
|
|
124
|
-
</section>
|
|
125
|
-
|
|
126
|
-
<section>
|
|
127
|
-
<h2>rk-recipe-card</h2>
|
|
128
|
-
<div class="grid">
|
|
129
|
-
<rk-recipe-card
|
|
130
|
-
recipe-id="52772"
|
|
131
|
-
name="Teriyaki Chicken Casserole"
|
|
132
|
-
image="https://www.themealdb.com/images/media/meals/wvpsxx1468256321.jpg"
|
|
133
|
-
category="Chicken"
|
|
134
|
-
area="Japanese"
|
|
135
|
-
minutes="45"
|
|
136
|
-
servings="4"
|
|
137
|
-
href="#52772"
|
|
138
|
-
>
|
|
139
|
-
<rk-badge tone="accent" small>weeknight</rk-badge>
|
|
140
|
-
<button class="demo" slot="actions">Add to plan</button>
|
|
141
|
-
</rk-recipe-card>
|
|
142
|
-
|
|
143
|
-
<rk-recipe-card
|
|
144
|
-
recipe-id="local-1"
|
|
145
|
-
name="Grandma's Sunday Ragù"
|
|
146
|
-
category="Pasta"
|
|
147
|
-
area="Italian"
|
|
148
|
-
minutes="180"
|
|
149
|
-
origin="custom"
|
|
150
|
-
favorite
|
|
151
|
-
>
|
|
152
|
-
<rk-badge tone="warning" small>slow cook</rk-badge>
|
|
153
|
-
</rk-recipe-card>
|
|
154
|
-
|
|
155
|
-
<rk-recipe-card
|
|
156
|
-
recipe-id="52959"
|
|
157
|
-
name="Baked Salmon with Fennel & Tomatoes"
|
|
158
|
-
image="https://www.themealdb.com/images/media/meals/1548772327.jpg"
|
|
159
|
-
category="Seafood"
|
|
160
|
-
area="British"
|
|
161
|
-
minutes="30"
|
|
162
|
-
compact
|
|
163
|
-
></rk-recipe-card>
|
|
164
|
-
</div>
|
|
165
|
-
</section>
|
|
166
|
-
|
|
167
|
-
<section>
|
|
168
|
-
<h2>rk-meal-slot</h2>
|
|
169
|
-
<div class="planner">
|
|
170
|
-
<rk-meal-slot day="monday" meal-type="breakfast" meal-label="Breakfast"></rk-meal-slot>
|
|
171
|
-
<rk-meal-slot
|
|
172
|
-
day="monday"
|
|
173
|
-
meal-type="dinner"
|
|
174
|
-
meal-label="Dinner"
|
|
175
|
-
recipe-id="52772"
|
|
176
|
-
recipe-name="Teriyaki Chicken Casserole"
|
|
177
|
-
recipe-image="https://www.themealdb.com/images/media/meals/wvpsxx1468256321.jpg"
|
|
178
|
-
></rk-meal-slot>
|
|
179
|
-
<rk-meal-slot day="tuesday" meal-type="lunch" meal-label="Lunch"></rk-meal-slot>
|
|
180
|
-
</div>
|
|
181
|
-
</section>
|
|
182
|
-
|
|
183
|
-
<section>
|
|
184
|
-
<h2>rk-tag-input</h2>
|
|
185
|
-
<rk-tag-input id="tags" label="Recipe tags" max-tags="6"></rk-tag-input>
|
|
186
|
-
</section>
|
|
187
|
-
|
|
188
|
-
<section>
|
|
189
|
-
<h2>rk-modal</h2>
|
|
190
|
-
<button class="demo" id="open-modal">Open dialog</button>
|
|
191
|
-
<rk-modal id="modal" heading="Delete this recipe?" subheading="This cannot be undone.">
|
|
192
|
-
<p style="margin: 0">Removing “Grandma's Sunday Ragù” also removes it from every day of your weekly plan.</p>
|
|
193
|
-
<button class="demo" slot="footer" id="cancel-modal">Cancel</button>
|
|
194
|
-
<button class="demo" slot="footer" style="background: var(--rk-danger); border-color: var(--rk-danger); color: #fff">Delete</button>
|
|
195
|
-
</rk-modal>
|
|
196
|
-
</section>
|
|
197
|
-
|
|
198
|
-
<section>
|
|
199
|
-
<h2>rk-empty-state</h2>
|
|
200
|
-
<rk-empty-state icon="🔍" heading="No recipes matched “quinoa risotto”" message="Try a broader search term, or clear the active filters.">
|
|
201
|
-
<button class="demo">Clear filters</button>
|
|
202
|
-
</rk-empty-state>
|
|
203
|
-
</section>
|
|
204
|
-
|
|
205
|
-
<section>
|
|
206
|
-
<h2>Event log</h2>
|
|
207
|
-
<pre id="log">Interact with the components above…</pre>
|
|
208
|
-
</section>
|
|
209
|
-
</main>
|
|
210
|
-
|
|
211
|
-
<script type="module">
|
|
212
|
-
const log = document.getElementById('log');
|
|
213
|
-
let lines = [];
|
|
214
|
-
const record = (event) => {
|
|
215
|
-
lines.unshift(`${event.type} → ${JSON.stringify(event.detail)}`);
|
|
216
|
-
log.textContent = lines.slice(0, 40).join('\n');
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const events = [
|
|
220
|
-
'rkSearch', 'rkClear', 'rkChipToggle', 'rkChipDismiss', 'rkSegmentChange', 'rkRatingChange',
|
|
221
|
-
'rkCardSelect', 'rkFavoriteToggle', 'rkMealAdd', 'rkMealSwap', 'rkMealRemove', 'rkMealOpen',
|
|
222
|
-
'rkMealDrop', 'rkTagsChange', 'rkModalClose',
|
|
223
|
-
];
|
|
224
|
-
events.forEach((name) => document.addEventListener(name, record));
|
|
225
|
-
|
|
226
|
-
// Array-valued properties are assigned as DOM properties, never attributes.
|
|
227
|
-
customElements.whenDefined('rk-segmented-control').then(() => {
|
|
228
|
-
const segments = document.getElementById('segments');
|
|
229
|
-
segments.options = [
|
|
230
|
-
{ label: 'Relevance', value: 'relevance' },
|
|
231
|
-
{ label: 'A–Z', value: 'az' },
|
|
232
|
-
{ label: 'Quickest', value: 'time', count: 12 },
|
|
233
|
-
{ label: 'Unavailable', value: 'x', disabled: true },
|
|
234
|
-
];
|
|
235
|
-
segments.value = 'relevance';
|
|
236
|
-
segments.addEventListener('rkSegmentChange', (event) => (segments.value = event.detail));
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
customElements.whenDefined('rk-tag-input').then(() => {
|
|
240
|
-
const tags = document.getElementById('tags');
|
|
241
|
-
tags.values = ['comfort food', 'one pot'];
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Favorite state is owned by the host app, exactly as in SvelteKit.
|
|
245
|
-
document.addEventListener('rkFavoriteToggle', (event) => {
|
|
246
|
-
event.target.favorite = event.detail.favorite;
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
document.addEventListener('rkChipToggle', (event) => {
|
|
250
|
-
event.target.selected = event.detail.selected;
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const modal = document.getElementById('modal');
|
|
254
|
-
document.getElementById('open-modal').addEventListener('click', () => (modal.open = true));
|
|
255
|
-
document.getElementById('cancel-modal').addEventListener('click', () => (modal.open = false));
|
|
256
|
-
modal.addEventListener('rkModalClose', () => (modal.open = false));
|
|
257
|
-
</script>
|
|
258
|
-
</body>
|
|
259
|
-
</html>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Config } from '@stencil/core';
|
|
2
|
-
|
|
3
|
-
export const config: Config = {
|
|
4
|
-
namespace: 'recipe-kit',
|
|
5
|
-
globalStyle: 'src/global/rk-global.css',
|
|
6
|
-
sourceMap: true,
|
|
7
|
-
buildEs5: false,
|
|
8
|
-
extras: {
|
|
9
|
-
enableImportInjection: true,
|
|
10
|
-
},
|
|
11
|
-
outputTargets: [
|
|
12
|
-
// Lazy-loaded distribution + `loader/` entry point. This is what the
|
|
13
|
-
// SvelteKit app consumes from npm via `recipe-kit-stencil/loader`.
|
|
14
|
-
{
|
|
15
|
-
type: 'dist',
|
|
16
|
-
esmLoaderPath: '../loader',
|
|
17
|
-
isPrimaryPackageOutputTarget: true,
|
|
18
|
-
copy: [{ src: 'global/rk-global.css', dest: 'rk-global.css', warn: true }],
|
|
19
|
-
},
|
|
20
|
-
// Standalone custom elements, for consumers that want to import a single
|
|
21
|
-
// component and define it themselves (tree-shakeable).
|
|
22
|
-
{
|
|
23
|
-
type: 'dist-custom-elements',
|
|
24
|
-
customElementsExportBehavior: 'auto-define-custom-elements',
|
|
25
|
-
externalRuntime: false,
|
|
26
|
-
generateTypeDeclarations: true,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
type: 'docs-readme',
|
|
30
|
-
footer: '*Built with [StencilJS](https://stenciljs.com/)*',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
type: 'www',
|
|
34
|
-
serviceWorker: null,
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
testing: {
|
|
38
|
-
browserHeadless: 'shell',
|
|
39
|
-
},
|
|
40
|
-
validatePrimaryPackageOutputTarget: true,
|
|
41
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"allowSyntheticDefaultImports": true,
|
|
4
|
-
"allowUnreachableCode": false,
|
|
5
|
-
"declaration": false,
|
|
6
|
-
"experimentalDecorators": true,
|
|
7
|
-
"lib": ["dom", "es2020"],
|
|
8
|
-
"moduleResolution": "bundler",
|
|
9
|
-
"module": "esnext",
|
|
10
|
-
"target": "es2020",
|
|
11
|
-
"noUnusedLocals": true,
|
|
12
|
-
"noUnusedParameters": true,
|
|
13
|
-
"sourceMap": true,
|
|
14
|
-
"strictNullChecks": false,
|
|
15
|
-
"strictPropertyInitialization": false,
|
|
16
|
-
"jsx": "react",
|
|
17
|
-
"jsxFactory": "h",
|
|
18
|
-
"jsxFragmentFactory": "Fragment",
|
|
19
|
-
"esModuleInterop": true,
|
|
20
|
-
"skipLibCheck": true
|
|
21
|
-
},
|
|
22
|
-
"include": ["src"],
|
|
23
|
-
"exclude": ["node_modules", "dist", "loader", "www"]
|
|
24
|
-
}
|
|
File without changes
|
/package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-badge/rk-badge.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-modal/rk-modal.css
RENAMED
|
File without changes
|
/package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-rating/rk-rating.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|