diff --git a/package-lock.json b/package-lock.json index 1e55c2aba..85328fb10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-semantic-version", "dependencies": { "@tanstack/angular-table": "^8.21.4", - "@tedi-design-system/core": "^6.9.0" + "@tedi-design-system/core": "^6.11.1" }, "devDependencies": { "@angular-devkit/build-angular": "20.3.27", @@ -12913,9 +12913,9 @@ } }, "node_modules/@tedi-design-system/core": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@tedi-design-system/core/-/core-6.9.0.tgz", - "integrity": "sha512-+RPnP8d9xcgabfFODJRd472JPzRIT8OR6XMd67RtbKyLOMNHKl8RwT22DCKcMnH4FEMv/v7Uay1jwgWL9Wp/Rg==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@tedi-design-system/core/-/core-6.11.1.tgz", + "integrity": "sha512-6ocg/DUodf5alJNkDDfVqs2LY5qazc2clfoU0WlTZ5p9u9PiiiThTo9l5/jfgwkLZcs5KkdSDNp3a/5z6VHBnA==", "engines": { "node": ">=24.0.0", "npm": ">=11.0.0" diff --git a/package.json b/package.json index 30cd3af46..3b6fc1059 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ }, "dependencies": { "@tanstack/angular-table": "^8.21.4", - "@tedi-design-system/core": "^6.9.0" + "@tedi-design-system/core": "^6.11.1" }, "devDependencies": { "@angular-devkit/build-angular": "20.3.27", diff --git a/skills/tedi-angular/references/components.md b/skills/tedi-angular/references/components.md index 5b36a2b83..85f3ca60b 100644 --- a/skills/tedi-angular/references/components.md +++ b/skills/tedi-angular/references/components.md @@ -110,10 +110,11 @@ the part of this document worth maintaining by hand. ### The `/tedi` and `/community` entry points collide -This is the highest-value trap in the Angular library and it has no React equivalent. **25 selectors +This is the highest-value trap in the Angular library and it has no React equivalent. **27 selectors are declared in both entry points, under identical class names**, including `tedi-card`, `tedi-modal`, `tedi-accordion`, `tedi-tabs`, `tedi-dropdown`, `tedi-form-field`, `tedi-pagination`, -`tedi-search`, `tedi-tag`, `[tedi-floating-button]`, and the checkbox/radio group family. +`tedi-search`, `tedi-tag`, `[tedi-floating-button]`, `tedi-file-dropzone`, and the checkbox/radio +group family. `CardComponent` from `/community` and `CardComponent` from `/tedi` are different components with different input APIs behind the same `` tag. Consequences: @@ -200,6 +201,59 @@ Both entry points declare `[tedi-floating-button]`. The Community component is `icon` input, not from projection, so you cannot swap in your own `tedi-icon` or add trailing content. It is decorative and `aria-hidden`, which means the projected text is the whole accessible name — if the icon carries meaning the text doesn't, compose a heading and an icon yourself. +- **`tedi-file-dropzone`'s `files` has no separate "default" input** — unlike the React version, which + needs `defaultFiles` alongside `files` to distinguish uncontrolled from controlled. The binding + picks the mode: `[files]` seeds the list and leaves the dropzone owning it, `[(files)]` keeps the + parent in sync, `[formControl]` hands it to the form. Bind a stable reference, though: an inline + literal (`[files]="[{ name: 'a.pdf' }]"`) is a new array on every check, so it is written back each + time and discards whatever the user added. +- **`tedi-file-dropzone` validates itself.** It registers on `NG_VALIDATORS`, so a `formControl` bound + to it fails with a `rejectedFiles` error (carrying the offending files) while any rejected file is + still listed — you don't add a validator for that. `Validators.required` alone would pass on a list + of visibly broken files whenever `keepRejectedFiles` is on and keeps them in the value. Same shape as Angular + Material's datepicker: the constraint comes from the component's own inputs (`accept`, `maxSize`), so + the component owns the rule. No `registerOnValidatorChange` is needed — `validate()` is a pure + function of the control value, which Angular already revalidates on every change. +- **`tedi-file-dropzone` emits `fileRemove` alongside `filesChange`.** `filesChange` gives the new list; + `fileRemove` gives the single entry the user deleted, so an in-flight upload can be aborted or a blob + URL revoked without diffing the two arrays. +- **`tedi-file-dropzone` selects files, it does not upload them.** It validates against `accept` and + `maxSize` and hands you the list; the upload, and setting each file's `isLoading` / `isValid` as it + progresses, is yours. A `validator` input adds a rule of your own on top, run on every selected file; + the reason it returns lands on that file's `error` after the built-in ones and joins the summary, + with the files it rejected named after it. Describe the rule in `hint`, which follows the allowed + types and size in the one hint. `keepRejectedFiles` decides what happens to a file that + fails `accept`, `maxSize` or `validator`: off (the default) discards it and summarises every rejection in one message under the + dropzone, which paints the border red. On keeps it in the list with its reason in that file's + `error`, leaving the border neutral — so the form value holds it with `isValid: false` and you must + filter on `isValid` before uploading. `error` is writable, so a failure the upload + came back with lands on the file it belongs to. +- **`tedi-file-dropzone` shows at most one hint and one validation message, error first.** Pass your + own text through `hint`, `error` and `success`, not projected `tedi-feedback-text`. `error` joins + the dropzone's own rejection summary in one error, and `success` takes the error's place while + there is none. +- **Selecting a file that is already listed is skipped**, matched on name, size and mtime — so two + different files sharing a name still both land. Project an `*tediFileDropzoneFile` template to + replace the built-in `tedi-attachment` row when a file needs a progress bar, an icon or its own + feedback. +- **`tedi-file-dropzone`'s `label` is the text inside the drop zone**, not a field label above it. It is + the accessible name of the zone, so the control is never nameless. Don't add a `