منتقي الملفات
يتيح للأشخاص اختيار ملفات بمنتقي الملفات الخاص بالمنصة، من زر، وحيث يتسع المكان من موضع يمكن أيضًا سحب الملفات إليه، ويقول القيود بالكلمات قبل الاختيار، ويحكم على ما اختير، ويسلّم منتجك الملفات التي قبلها والملفات التي رفضها. وهو لا يحتفظ بشيء، ولا يسرد شيئًا، ولا يشارك في أي نموذج.
متقدّم
معظم المنتجات تستخدم Chat composer, File upload، وهذا المكوّن جزء منه.
- الفئة
- الاختيار
- الرموز
comp.file-picker- المستوى
- أساسي
- يُستخدم في
- Chat composer, File upload
- Web
tanrei-file-pickerschedule - SwiftUI
TanreiFilePickerradio_button_unchecked - Jetpack Compose
TanreiFilePickerradio_button_unchecked - Flutter
TanreiFilePickerradio_button_unchecked - React Native
FilePickerradio_button_unchecked
مولَّد من docs/status/status.json, specs/file-picker.spec.json
منتقي الملفات بكل تخطيط. اختيار ملف لا يغيّر شيئًا هنا: فالصفحة لا تحتفظ بأي ملف.
منتقي الملفات (file picker) طريقة اختيار الملفات، ولا شيء غير ذلك. زر من عندك يفتح منتقي الملفات الخاص بالمنصة. ويمكن أن تحيط به منطقة إفلات (drop area) تقبل أيضًا الملفات التي تُسحب إليها. والقيود تقف بجانبه بكلماتك. وما اختير يُحكم عليه بحسب القيود ثم يُسلَّم إليك: الملفات التي قُبلت مع الحدث select، والمرفوضة مع الحدث reject.
هو مكوّن متقدم: عنصر التحكم مجرَّدًا، لمنتج يحتفظ بالملفات بنفسه. فهو لا يحتفظ بأي ملف، ولا يرسم أي قائمة، ولا يرسل شيئًا مع نموذج. ومعظم المنتجات تريد مكوّن رفع الملفات، وهو هذا المنتقي وتحته قائمة من عناصر الملفات، جاهزًا للاستخدام في نموذج.
يُستخدم في مكوّن رفع الملفات. واستخدمه وحده، من دون مكوّن رفع الملفات، حين تذهب الملفات إلى مكان ليس قائمة مكوّن رفع الملفات:
- ملف واحد يُقرأ في الحال ولا يُحتفظ به: استيراد ملف إعدادات، أو جدول بيانات فيه صفوف، أو صورة تُقتطع.
- ملفات تُعرض في مكان آخر: مرفقات داخل رسالة تُكتب، أو على لوحة رسم، أو في جدول من عندك.
- زر إرفاق بلا منطقة وبلا نصوص، في شريط أدوات.
ولقبول الملفات المُفلَتة في أي مكان من شاشة كاملة، أحِط الشاشة بساحة إفلات، وأبقِ منتقي الملفات في تلك الشاشة طريقةً للاختيار من دون سحب.
لا يحتفظ بشيء
بعد الحدث select يكون منتقي الملفات قد نسي الملفات. والاختيار الثاني يُحكم عليه كأنه الأول. فإن لم يحتفظ منتجك بما يعطيه إياه select، لم يحتفظ به أحد.
على الويب هو <tanrei-file-picker>. والمنصات الأخرى لم تنفّذه بعد.
ضع زرًّا واحدًا في المنفذ trigger واستمع إلى الحدث select.
<tanrei-field label="ملف الطبقة" supporting-text="تحلّ هذه الطبقة محل الطبقة التي تعدّلها.">
<tanrei-file-picker id="layer" accept=".json" constraints-text="ملف JSON واحد"
selection-text="لم يُختر أي ملف">
<tanrei-button slot="trigger" variant="outlined">اختيار ملف</tanrei-button>
</tanrei-file-picker>
</tanrei-field>
<script type="module">
import '@tanrei/web/components/field';
import '@tanrei/web/components/file-picker';
import '@tanrei/web/components/button';
const picker = document.querySelector('#layer');
picker.addEventListener('select', async ({ detail }) => {
const [file] = detail.files;
// المنتقي لا يحتفظ بشيء. قل أنت ما الذي اختير، وافعل بالملف ما تريده.
picker.selectionText = file.name;
importLayer(JSON.parse(await file.text()));
});
// للملف المرفوض سبب هو اسم لا جملة. قله بكلماتك.
picker.addEventListener('reject', ({ detail }) => {
const field = picker.closest('tanrei-field');
field.errorText = detail.files[0].reason === 'type' ? 'اختر ملف JSON.' : 'اختر ملفًا واحدًا.';
field.error = true;
});
</script>// يُستورد المكوّن المتقدّم، وهو جزء تُبنى منه مكوّنات أخرى، من المدخل advanced.
import { Button, Field } from '@tanrei/react';
import { FilePicker } from '@tanrei/react/advanced';
<Field label="ملف الطبقة" error={!!problem} errorText={problem}>
<FilePicker accept={['.json']} constraintsText="ملف JSON واحد"
selectionText={chosen?.name ?? 'لم يُختر أي ملف'}
onSelect={(event) => importLayer(event.detail.files[0])}
onReject={(event) => explain(event.detail.files)}>
<Button slot="trigger" variant="outlined">اختيار ملف</Button>
</FilePicker>
</Field>الملفات التي تصل إلى منتجك بطريق آخر، كالملصوقة أو المُفلَتة على جزء آخر من الصفحة، تحكم عليها الدالة judge() بالقاعدة نفسها. وهي تعيد الجواب ولا يصدر عنها أي حدث:
const { accepted, refused } = picker.judge(event.clipboardData.files);في React تكون judge() على المرجع (ref): ref.current.judge(files).
المشغِّل وحده؛ ثم مع ما اختير ومع القيود؛ ثم منطقة إفلات في حالة الخطأ؛ ثم معطَّل
البنية
| الجزء | الوصف |
|---|---|
1 group | The whole component: the trigger with its texts, or the drop area. Exposed as a named group while it has a name; it draws nothing of its own. |
2 trigger | The product's own Button in the trigger slot. Activating it opens the platform's own file picker. It is the one way to choose by keyboard, by touch and with assistive technology, in both layouts. |
3 container · اختياري | The drop area, in the drop-area layout: a bounded place around the glyph, the prompt, the trigger and the line of limits, which takes files dragged onto it and opens the picker when pressed. It carries the state answer. |
4 outline · اختياري | The drop area's edge, dashed or solid as the system says. |
5 icon · اختياري | The glyph at the top of the drop area: the icon set's upload glyph, or the one in the icon slot. Decorative. |
6 label · اختياري | The drop area's prompt (dropText): the words that say files can be dragged here. |
7 supporting-text · اختياري | The line that states the limits in words (constraintsText): which kinds of file, how large, how many. In the area, or below the trigger. |
8 selection-text · اختياري | In the button layout, the text beside the trigger that says what is chosen now (selectionText). Whoever holds the files writes it. |
الخصائص
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
layout | button | drop-area | "button" | How choosing is offered. button: the trigger alone, with the line of limits below it; for a form among other fields, a narrow place, a phone, a toolbar. drop-area: a bounded place around the trigger that also takes files dragged onto it and opens the picker when pressed; for a screen whose main task is to hand over files, where a pointer is likely. Both open the same picker from the same trigger. |
multiple | boolean | false | Whether more than one file can be chosen at a time. False: the picker offers one file, and a drop of several is refused whole. True: the picker offers several. The file picker keeps nothing, so whether a second choice adds to the first or takes its place is decided by whoever keeps the files. |
accept | strings | | The kinds of file that are taken: file name extensions with their dot (".pdf") and media types, a whole family as "image/*". The picker is asked to offer these, and a file of another kind — dropped, or chosen in a picker that did not filter — is refused (reject, reason type). Empty takes every kind. It enforces the limit; constraintsText says it. |
maxSize | number | null | The largest size of one file, in bytes. A larger file is refused (reject, reason size). Null sets no limit. It is judged from the size the platform reports; the file is not read. |
maxFiles | number | null | The most files one choice or one drop may bring, while multiple is true. Files beyond it are refused (reject, reason count). Null sets no limit; ignored while multiple is false. The file picker counts nothing across choices: a product that keeps a list sets this to the room that is left — zero once the list is full, which refuses every file — or judges the room itself. |
label | string | "" | The accessible name of the file picker as a group ("Photo"). It is not drawn. Leave it empty inside a Field, whose label names the group and is drawn, and inside a component that names the whole; left empty elsewhere, no group is exposed and the trigger stands as the button it is. |
constraintsText | string | "" | The limits, in the product's words, shown before the choice ("PDF or PNG, up to 10 MB each, 5 files at most"). Drawn in the area, or below the trigger, and given to assistive technology as the trigger's description. Tanrei builds no such sentence from accept, maxSize and maxFiles: it has no words. |
dropText | string | "" | The drop area's prompt, in the product's words ("Drag files here, or choose them"). Drawn above the trigger in the drop-area layout; ignored in the button layout. |
selectionText | string | "" | What stands beside the trigger in the button layout: what is chosen now, in the product's words or the file's own name ("No file chosen", "brief.pdf", "3 files"). Read with the trigger, as part of its description. The file picker keeps nothing and so never writes or changes it: whoever holds what was chosen says it, and leaves it empty where a list below already does. Ignored in the drop-area layout. |
disabled | boolean | false | Nothing can be chosen or dropped: the trigger is disabled and the area answers nothing. Inside a Field, disable the Field. |
error | boolean | false | Puts the file picker in the error condition: the drop area's edge is drawn in the error role and the trigger is exposed as invalid. A Field sets it with its own error; the words are the Field's errorText. The file picker never sets it itself. |
required | boolean | false | Exposes the trigger as required, where the platform's button has such a state. A Field that is required sets it. It draws nothing — the mark beside the label is the Field's — and it validates nothing: the file picker has no value that could be missing. |
المنافذ
| المنفذ | الوصف |
|---|---|
trigger | The product's own Button that opens the picker, with the product's own label ("Choose files"), variant and size. Exactly one. The file picker gives it the behaviour and never restyles it. |
icon | A glyph in place of the upload glyph at the top of the drop area (a picture for a photo, a table for a spreadsheet). Decorative; ignored in the button layout. |
الأحداث
| الحدث | الوصف |
|---|---|
select | Files were chosen in the picker or dropped on the area, and passed accept, maxSize and the count of one choice. Emitted once per choice, with those files as the platform's own handles, in the order the platform gave them. The file picker keeps none of them and reads none: the product, or the component that holds the file picker, does with them what it means to. |
reject | Files of one choice or one drop were refused. Emitted once, after select where both happen, with each refused file and its reason: type (not among accept, or a folder), size (larger than maxSize) or count (more than one while multiple is false, or more than maxFiles in one choice). The reasons are names for the product's code, not words: the product says what happened, in its own words and in a place of its own — a Field's errorText, a row of its list, a message it has announced. The file picker draws and says nothing for a refusal. |
الحالات
السلوك
متطلبات بصيغة EARS. يُختبر تنفيذ كل منصة مقابل هذه المعرّفات. يُعرض نص المتطلبات كما ورد في ملف المواصفة، بالإنجليزية.
| المعرّف | المتطلب |
|---|---|
FPK-1 | THE file picker SHALL lay out one part, from its start edge: the trigger with its texts WHILE layout is button (FPK-2), the drop area WHILE layout is drop-area (FPK-3). It SHALL be as wide as its layout gives it and as tall as its parts, SHALL draw no container, fill or edge around the whole, SHALL draw no list and no row for what was chosen, and SHALL draw no label, no help, no error text and no requirement mark of its own: those are the Field's (FPK-21). |
FPK-2 | WHILE layout is button THE file picker SHALL draw the trigger at its start edge at the size the product gave it and, WHILE selectionText is not empty, selectionText after it on the trigger's line, comp.file-picker.trigger.gap from it and centred on it, in comp.file-picker.selection-text.typescale and comp.file-picker.standard.selection-text.color, wrapping below the trigger where the line is too short; and WHILE constraintsText is not empty SHALL draw it comp.file-picker.supporting-text.gap below that line, from the start edge, in comp.file-picker.supporting-text.typescale and comp.file-picker.standard.supporting-text.color, on as many lines as it needs and never cut. WHILE layout is button THE file picker SHALL draw no area, edge or glyph, SHALL ignore dropText and the icon slot, and SHALL take no drop (FPK-13). THE file picker SHALL NOT write selectionText itself and SHALL NOT change it because of a choice: it keeps nothing (FPK-7), so what is chosen now is said by whoever holds it. |
FPK-3 | WHILE layout is drop-area THE file picker SHALL draw the drop area across its whole width: a container with corners of comp.file-picker.container.shape, filled with comp.file-picker.standard.container.color, with an edge comp.file-picker.outline.width wide in comp.file-picker.standard.outline.color drawn inside its size, and inside it — comp.file-picker.container.padding-block from its top and bottom and comp.file-picker.container.padding-inline from its sides — one column, each part centred on the inline axis and comp.file-picker.container.gap from the next: the glyph (FPK-4), dropText WHILE it is not empty in comp.file-picker.label.typescale and comp.file-picker.standard.label.color, the trigger at the size the product gave it, and constraintsText WHILE it is not empty in comp.file-picker.supporting-text.typescale and comp.file-picker.standard.supporting-text.color. Each text SHALL be centred, wrap onto as many lines as it needs and never be cut. The area SHALL be as tall as its parts and paddings and have no height of its own. WHILE layout is drop-area THE file picker SHALL NOT draw selectionText: what was dropped on a place is shown below it, by whoever holds it. |
FPK-4 | THE file picker SHALL draw the drop area's edge as comp.file-picker.outline.style names: dashed, as dashes comp.file-picker.outline.dash-length long with gaps of the same length, following the corners; solid, as one unbroken line. It SHALL draw at the top of the area the icon set's upload glyph, or the glyph in the icon slot WHILE that slot holds one, at comp.file-picker.icon.size in comp.file-picker.standard.icon.color, with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, and SHALL keep it from assistive technology. A platform that cannot draw a dashed edge along eased corners SHALL draw it solid and state that in its README. |
FPK-5 | THE file picker SHALL take as its trigger the one element in the trigger slot — a Button of the product's, with the product's label, variant and size — and SHALL leave it what it is: drawn from the Button's own tokens, answering hover, focus and press itself, named by its own label. WHEN the trigger is activated, by any input the Button takes, THE file picker SHALL open the platform's own file picker (FPK-6). IF the trigger slot is empty THE file picker SHALL still render and still take drops and presses on its area, and in debug builds SHALL report that files cannot be chosen by keyboard, by touch or with assistive technology, which is a functional failure; IF it holds more than one element THE file picker SHALL use the first and report the others. |
FPK-6 | WHEN the picker is asked for THE file picker SHALL open the platform's own file picker — never a list of files it draws itself — asking it for one file WHILE multiple is false and for any number WHILE multiple is true, and to offer the kinds accept lists WHILE accept is not empty. WHEN the person chooses, THE file picker SHALL judge what was chosen (FPK-9) and leave focus on the trigger; WHEN the person leaves the picker without choosing THE file picker SHALL emit nothing, change nothing and leave focus on the trigger. WHILE a picker it opened is still open THE file picker SHALL NOT open a second. |
FPK-7 | THE file picker SHALL keep nothing and send nothing: it SHALL open no connection and send no file or part of one to anyone, SHALL NOT read a file's bytes — not to check its kind, not to measure it, not to draw a picture of it — SHALL NOT copy or store a file, and SHALL NOT hold a file, or any record of an earlier choice, beyond the turn in which it hands the files of one choice over (FPK-9): a second choice SHALL be judged as if it were the first. What is known of a file SHALL be what the platform reports of it without opening it: its name, its size and its kind. THE file picker SHALL take no part in a form: it has no name and no value, submits nothing, reports no missing value, and has nothing to reset or to restore. The files of a form are submitted by the component that keeps them, or sent by the product. |
FPK-8 | WHEN the drop area of the file picker is pressed by pointer or touch THE file picker SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. The area answers no key (FPK-12): Enter and Space press the trigger, which answers as a Button does (BTN-8). |
FPK-9 | WHEN files arrive — chosen in the picker or dropped on the area — THE file picker SHALL judge them in the order the platform gave them, by what the platform reports without opening them, and keep none: WHILE multiple is false and more than one arrived, it SHALL refuse them all with the reason count, choosing none for the person; otherwise it SHALL refuse with the reason type a file whose kind matches no entry of a non-empty accept — an extension compared with the end of its name without regard to case, a media type with the kind the platform reports, a family such as image/* with every kind of that family — and anything that is a folder; SHALL refuse with the reason size a file larger than maxSize WHILE maxSize is a number; and, WHILE multiple is true and maxFiles is a number, SHALL refuse with the reason count each remaining file beyond the first maxFiles of them. It SHALL then emit select once with the files that passed, WHERE there are any, and reject once with the refused files and their reasons, WHERE there are any, in that order. THE file picker SHALL judge each arrival by itself: it knows of no earlier choice, so the room that is left in a list is judged by whoever keeps the list. THE file picker SHALL draw no row and no error and say no word because of a choice. |
FPK-10 | WHILE the drop area is pressed THE file picker SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact; dim applies the pressed opacity to the whole area and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; outline, glyph, texts, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.file-picker.motion.feedback-duration with comp.file-picker.motion.state-easing, fades over comp.file-picker.motion.state-duration with comp.file-picker.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. |
FPK-11 | THE file picker SHALL ease the corners of its drop area by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. THE file picker SHALL draw no focus indicator of its own: keyboard focus is on the trigger, which shows it as sys.behavior.focus-style names, as every Button does (BTN-10). |
FPK-12 | WHEN the person presses the drop area outside the trigger, by pointer or touch, WHILE disabled is false THE file picker SHALL do what activating the trigger does (FPK-6) and move focus to the trigger, so that the whole area is one large target for the one action. The area SHALL NOT be a stop in the focus order, SHALL have no role and no name of its own, and SHALL answer no key: Enter and Space are the trigger's. A press that begins on the trigger SHALL be the trigger's alone and open the picker once. |
FPK-13 | WHILE something is dragged over the drop area and disabled is false THE file picker SHALL draw the area as receiving — filled with comp.file-picker.receiving.container.color, its edge in comp.file-picker.receiving.outline.color, its glyph, its prompt and its line of limits in comp.file-picker.receiving.content.color — and tell the platform that letting go here copies; or, WHERE the platform tells before the drop that nothing dragged can be taken — it holds no file, every file is of a kind accept refuses, or it holds more than one file while multiple is false — as refusing: the fill and the texts as at rest, the edge in comp.file-picker.refusing.outline.color, and the icon set's block glyph in the glyph's place in comp.file-picker.refusing.icon.color, telling the platform that letting go here does nothing. The condition SHALL begin when the drag enters the area's bounds and end when it leaves them or ends, not when it crosses a part inside the area; the trigger and the texts SHALL NOT answer the drag themselves; and a drag elsewhere on the page or the screen SHALL change nothing: the file picker watches no more than its own area. WHILE layout is button THE file picker SHALL take no drop and SHALL leave a drag over it to the platform and the product. |
FPK-14 | WHEN files are let go over the drop area WHILE disabled is false THE file picker SHALL end the condition of FPK-13 at once, take the drop so that the platform does not open or navigate to the file, and judge what was dropped exactly as a choice in the picker (FPK-9): a drop over a refusing area SHALL emit reject with what it holds and no select, and a drop that holds no file SHALL emit nothing. It SHALL NOT move focus because of a drop. WHILE disabled is true THE file picker SHALL draw neither condition, tell the platform that letting go here does nothing, and still take a drop over its area so that a file let go by mistake does not replace the page. |
FPK-15 | WHILE the pointer is over the drop area, or the area is pressed (FPK-12), THE file picker SHALL render the area's container, edge, glyph, prompt, line of limits and opacity from the component tokens of the highest-precedence active state, with precedence pressed over hovered, and the pointer over the trigger counts as over the area. THE drop area SHALL have no focused state: keyboard focus is on the trigger, which shows its own indicator. Among conditions, disabled SHALL win over refusing and receiving, which win over hovered and pressed; the error condition (FPK-16) SHALL change the edge alone and combine with every state. |
FPK-16 | WHILE error is true THE file picker SHALL draw the drop area's edge in comp.file-picker.standard.error.outline.color, in every state, and every other part as without the error; SHALL expose the trigger as invalid; and WHILE layout is button SHALL draw nothing for it, because the error is said by the words of the Field around it. THE file picker SHALL never set error itself: not for a refused file and not for a picker that was left without a choice. WHILE something is dragged over the area the conditions of FPK-13 SHALL be drawn in the error edge's place. |
FPK-17 | WHILE label is not empty, or a Field names it (FPK-21), THE file picker SHALL expose itself to assistive technology as a group of that name holding the trigger; WHILE neither gives a name THE file picker SHALL expose no group and no role of its own, and SHALL report nothing: its trigger is a button with a name of its own, and a component that holds the file picker names the whole itself. It SHALL give the trigger, after any description the trigger has of its own, a description made of constraintsText, then selectionText WHILE it is drawn (FPK-2), then what a Field or the component that holds it gives (FPK-21), and SHALL expose the trigger as required WHILE required is true and as invalid WHILE error is true. It SHALL NOT expose the drop area, its glyph or dropText as controls: dropText is read as the text it is, before the trigger. THE file picker SHALL have no live region and SHALL announce nothing: a choice is said by whoever keeps it. |
FPK-18 | THE file picker SHALL have no words of its own: it SHALL take every word it draws or says — the trigger's label, label, constraintsText, dropText and selectionText — from the product alone, by the trigger and the props of the instance, and SHALL read none of them from the product's words by a key, because none is said the same way wherever a file picker stands. IF dropText or selectionText is not given THE file picker SHALL draw nothing in its place. IF accept is not empty, or maxSize or maxFiles is a number, WHILE constraintsText is empty THE file picker SHALL still render and in debug builds report that a limit is enforced and not said. THE file picker SHALL build no sentence from accept, maxSize, maxFiles, a count of files or a size in bytes. |
FPK-19 | WHILE disabled is true THE file picker SHALL disable its trigger as that Button's own disabled does and give it back as it was WHEN disabled becomes false, never enabling a trigger the product disabled itself; SHALL render the drop area's container, edge, glyph, prompt and line of limits, and selectionText, from their disabled tokens; SHALL NOT answer a press on the area (FPK-12) or a drag over it (FPK-14); and SHALL emit neither select nor reject. The way of FPK-31 SHALL still answer: disabled stops the person, not the product. |
FPK-20 | WHEN the colors of the drop area change between states or conditions THE file picker SHALL animate the change over comp.file-picker.motion.state-duration with comp.file-picker.motion.state-easing, and SHALL change them at once where motion is reduced. WHEN selectionText appears, changes or leaves THE file picker SHALL draw that at once, without a motion of its own. |
FPK-21 | WHILE the file picker is the control of a Field THE file picker SHALL take from it what a Field gives its control (FLD-8, FLD-10, FLD-11, FLD-14): the Field's label as its group's name WHILE its own label is empty; the Field's supporting text with, in error, the error text after it and optional text as the last part of the trigger's description; the invalid state as its own error condition (FPK-16); the required state as required; and the disabled state as disabled. WHEN the label of the Field is pressed THE file picker SHALL move focus to its trigger and open nothing. WHILE another component holds the file picker as its member THE file picker SHALL take the same from that component by the same exchange — a name or none, a description, and the three states, each as a value — so that it is tied inside a component exactly as it is tied by hand. |
FPK-22 | WHILE the text size is enlarged THE file picker SHALL grow with its texts: the prompt, the line of limits and selectionText wrap and are never cut; the glyph keeps its box; the area grows in height; and the trigger grows as the Button says. At 200 % in a width of 320 no text SHALL be lost or lie over another part, and nothing SHALL need scrolling in two directions. |
FPK-23 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE file picker SHALL draw the drop area's edge and its texts in the platform's text color and no fill; SHALL draw the receiving area with an edge twice comp.file-picker.outline.width wide, solid, in the platform's highlight color, and the refusing area with its block glyph, so that neither rests on a fill the mode removes; and SHALL draw disabled parts in the platform's disabled text color. |
FPK-24 | WHERE the layout direction is right-to-left THE file picker SHALL mirror its layout: the trigger at the right, selectionText after it toward the left, and the line of limits from the right edge. The drop area's column stays centred. A ripple SHALL still grow from the point of contact. |
FPK-25 | THE file picker SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the file picker is shown THE file picker SHALL draw itself for the new direction at once, without a motion of its own, keeping its focus. |
FPK-26 | THE file picker SHALL lay out each text it draws — the prompt, the line of limits and selectionText — as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a file's name given as selectionText keeps its order, its dot and its extension in a right-to-left layout. WHERE the layout direction is right-to-left a glyph SHALL be mirrored exactly when the system's icon set marks its name as mirrored or the product forces it, by the icon helper and inside its own box; THE file picker SHALL mirror no glyph itself. |
FPK-27 | WHEN layout names a value the file picker does not know THE file picker SHALL behave as button; IF maxSize is not a number above zero THE file picker SHALL set no limit of size; IF maxFiles is not a whole number of zero or more THE file picker SHALL set no limit of count; IF an entry of accept is neither an extension with its dot nor a media type THE file picker SHALL ignore that entry; and in each case debug builds SHALL report it. WHILE maxFiles is zero and multiple is true THE file picker SHALL refuse every file that arrives with the reason count and report nothing: zero is the room a full list has left, and to read it as no limit would open the list when it is full. |
FPK-28 | WHEN a prop of the file picker changes while it is shown THE file picker SHALL draw and expose the new value at once, keeping its focus, and SHALL announce nothing. WHEN accept, maxSize, maxFiles or multiple changes THE file picker SHALL judge by the new value from the next arrival on. WHEN layout changes while something is dragged over the area THE file picker SHALL end the condition. |
FPK-29 | WHILE the system's icon set cannot yet draw a glyph of the file picker (its font is still loading) THE file picker SHALL draw nothing in that glyph's place, never the glyph's name as text, SHALL keep the glyph's box, and SHALL draw the glyph once the set can. |
FPK-30 | IF the trigger slot holds an element that is not a Button THE file picker SHALL still open the picker when that element is activated by a press or a click, and in debug builds report it; IF the file picker holds a child that is neither its trigger nor its glyph THE file picker SHALL NOT draw it and in debug builds report it. A platform that cannot inspect its content SHALL state the rule in its README and docs page. |
FPK-31 | THE file picker SHALL offer the product, and a component that holds it, a way in the platform's idiom to have files judged that came neither through its picker nor onto its area — files pasted, dropped on another part of the product's screen, or made by the product: it SHALL judge them as one arrival by the rule of FPK-9 and answer with the files that passed and with the refused files and their reasons, each in the order given; SHALL emit neither select nor reject for them, as a control's events tell what a person did; and SHALL keep none of them (FPK-7). |
FPK-32 | WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE file picker SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on its group (FPK-17), which it SHALL be WHILE it has a name: its label or its Field's, or one given so — an id reference by the elements it names in the host's tree or a tree around it, a description after its own. WHILE label, or the Field's label, is not empty it is the group's name, over a name given so. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE file picker SHALL take it off that element. |
مطابقة الأسماء بين المنصات
التكافؤ لا يعني واجهات برمجية متطابقة. تعبّر كل منصة عن العقد نفسه بأسلوبها.
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| المكوّن | tanrei-file-picker | TanreiFilePicker | TanreiFilePicker | TanreiFilePicker | FilePicker |
layout · الخاصية | layout | layout | layout | layout | layout |
multiple · الخاصية | multiple | allowsMultipleSelection | multiple | multiple | multiple |
accept · الخاصية | accept | allowedContentTypes | mimeTypes | accept | accept |
maxSize · الخاصية | maxSize | maxSize | maxSize | maxSize | maxSize |
maxFiles · الخاصية | maxFiles | maxFiles | maxFiles | maxFiles | maxFiles |
label · الخاصية | label | label | contentDescription | semanticLabel | accessibilityLabel |
constraintsText · الخاصية | constraintsText | constraintsText | constraintsText | constraintsText | constraintsText |
dropText · الخاصية | dropText | dropText | dropText | dropText | dropText |
selectionText · الخاصية | selectionText | selectionText | selectionText | selectionText | selectionText |
disabled · الخاصية | disabled | disabled | enabled | enabled | disabled |
error · الخاصية | error | isError | isError | isError | error |
required · الخاصية | required | isRequired | required | required | required |
select · الحدث | select | onSelect | onSelect | onSelect | onSelect |
reject · الحدث | reject | onReject | onReject | onReject | onReject |
| ملاحظات | The shadow-root form: a control whose parts and states are its own. <tanrei-file-picker accept=".json" constraints-text="…"><tanrei-button slot="trigger" variant="outlined">Choose a file</tanrei-button></tanrei-file-picker>. Attributes are kebab-case (max-size, max-files, constraints-text, drop-text, selection-text); accept is a comma-separated list, the form a native file input takes. The trigger and the glyph stay in the product's light DOM (the trigger and icon slots); the area and the texts are in the shadow root. The picker is a real <input type="file"> in the shadow root — hidden from sight, from the focus order and from assistive technology — carrying multiple and accept, whose click() the trigger's click and a press on the area call (both are user activations); its change and the area's drop feed FPK-9, and its value is cleared after each choice so that the same file can be chosen twice. The area listens for dragenter, dragover, dragleave and drop on itself only, counts enters and leaves so that a child does not end the condition, sets dropEffect to copy or none, and during a drag knows what the browser tells: kinds and a count (DataTransfer.items), never names or sizes, so a file that is too large is refused at the drop. select and reject are CustomEvents that bubble: detail.files is a File[], and reject's detail.files is { file, reason }[]. judge(files: Iterable<File>): { accepted: File[], refused: { file: File, reason: 'type' | 'size' | 'count' }[] } is FPK-31 and dispatches nothing. **Not form-associated**: no name, no value, no validity (FPK-7). It is a Field's control: it asks for the Field's connection as every Tanrei control does and takes the name, the description and the three states as values; a component that holds it gives the same values through the same connection. The description, aria-invalid and disabled are written on the slotted trigger's host — which a tanrei-button carries to its native button — and taken back when the trigger leaves the slot; the group's role and name are on an element of the shadow root, present only while there is a name (FPK-17). A button's role has no required state, so required is said only by a Field's required text in that description. Parts: container, icon, label, supporting-text, selection-text. Custom properties: --<namespace>-comp-file-picker-…. Without script, and until the element is defined, the trigger is a button that opens nothing. Class TanreiFilePicker. React: FilePicker, with onSelect, onReject, and a ref with judge(). | TanreiFilePicker(layout: .dropArea, onSelect: { urls in … }) { TanreiButton("Choose files") { } }. The picker is fileImporter(isPresented:allowedContentTypes:allowsMultipleSelection:); accept is a list of UTType (an extension or a media type is resolved to one), and the files are security-scoped URLs the product opens and releases — the view holds none after onSelect returns (FPK-7). The drop area is dropDestination(for: URL.self) with its isTargeted binding for the receiving condition; SwiftUI tells the kinds before the drop, not sizes. With VoiceOver the area is a drop point named by dropText. disabled follows .disabled(_:) and the environment; a Field's name, description and states arrive through the environment, and a view that holds the picker sets the same environment values. FPK-31 is TanreiFilePicker.judge(_:accept:maxSize:maxFiles:multiple:), a static function of the limits, since a view has no instance to call. No form. | TanreiFilePicker(onSelect = { uris -> … }, trigger = { TanreiButton(onClick = {}) { Text("Choose files") } }). The picker is rememberLauncherForActivityResult with OpenDocument or OpenMultipleDocuments; accept is given as MIME types (an extension is resolved through MimeTypeMap), and the files are content Uris whose name and size come from the resolver's metadata, not from reading them. The drop area is Modifier.dragAndDropTarget, which is meaningful on large screens and with a pointer; on a phone the drop-area layout is still drawn and still pressed. disabled is the inverse flag enabled. The group is semantics(mergeDescendants = false) { contentDescription }, set only while there is a name. FPK-31 is rememberTanreiFilePickerState().judge(uris), or the top-level judgeFiles(uris, limits) it calls. No form. | The framework has no file picker and no drop target for files; both are plugins, and the package depends on none. TanreiFilePicker(trigger: TanreiButton(…), onPick: (request) async => …, onSelect: …): onPick is the product's function that opens whatever picker it uses with the request's multiple and accept and returns the files as TanreiPickedFile(name, size, mimeType, handle); the file picker judges them by FPK-9 and emits. Drops arrive the same way: the product's drop plugin calls TanreiFilePickerController.dragEntered / dragExited / dropped(files), and without it the drop-area layout is drawn and pressed and takes no drop — the README says so. TanreiFilePickerController.judge(files) is FPK-31. disabled is the inverse flag enabled. No form: it is no FormField. | React Native has no file picker of its own; the package depends on no picker library. <FilePicker trigger={<Button label="Choose files" />} onPick={async ({ multiple, accept }) => files} onSelect={…} />: onPick is the product's function that opens its picker and returns { name, size, type, uri }[]; the file picker judges them by FPK-9 and emits. No drop of files on phones: the drop-area layout is drawn and pressed and takes no drop, and the README says so. A ref holds judge(files) (FPK-31). No form. |
الرموز
| الرمز | يشير إلى | القيمة المحلولة (فاتح) |
|---|---|---|
--tanrei-comp-file-picker-container-gap | sys.space.space150 | 12px |
--tanrei-comp-file-picker-container-padding-block | sys.space.space400 | 32px |
--tanrei-comp-file-picker-container-padding-inline | sys.space.space200 | 16px |
--tanrei-comp-file-picker-container-shape | sys.shape.corner.medium | 8px |
--tanrei-comp-file-picker-disabled-opacity | — | 1 |
--tanrei-comp-file-picker-focused-opacity | — | 1 |
--tanrei-comp-file-picker-hovered-opacity | — | 1 |
--tanrei-comp-file-picker-icon-size | sys.icon.large | 32px |
--tanrei-comp-file-picker-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-file-picker-motion-state-duration | sys.motion.role.state.large | 250ms |
--tanrei-comp-file-picker-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-file-picker-outline-dash-length | sys.space.space50 | 4px |
--tanrei-comp-file-picker-outline-style | — | 0 |
--tanrei-comp-file-picker-outline-width | sys.border.hairline | 1px |
--tanrei-comp-file-picker-pressed-opacity | — | 1 |
--tanrei-comp-file-picker-receiving-container-color | sys.color.secondary-container | |
--tanrei-comp-file-picker-receiving-content-color | sys.color.on-secondary-container | |
--tanrei-comp-file-picker-receiving-outline-color | sys.color.on-secondary-container | |
--tanrei-comp-file-picker-refusing-icon-color | sys.color.error | |
--tanrei-comp-file-picker-refusing-outline-color | sys.color.error | |
--tanrei-comp-file-picker-standard-container-color | — | |
--tanrei-comp-file-picker-standard-disabled-container-color | — | |
--tanrei-comp-file-picker-standard-disabled-error-outline-color | — | |
--tanrei-comp-file-picker-standard-disabled-icon-color | — | |
--tanrei-comp-file-picker-standard-disabled-label-color | — | |
--tanrei-comp-file-picker-standard-disabled-outline-color | — | |
--tanrei-comp-file-picker-standard-disabled-selection-text-color | — | |
--tanrei-comp-file-picker-standard-disabled-state-layer-color | — | |
--tanrei-comp-file-picker-standard-disabled-supporting-text-color | — | |
--tanrei-comp-file-picker-standard-error-outline-color | sys.color.error | |
--tanrei-comp-file-picker-standard-focused-container-color | — | |
--tanrei-comp-file-picker-standard-focused-error-outline-color | — | |
--tanrei-comp-file-picker-standard-focused-icon-color | — | |
--tanrei-comp-file-picker-standard-focused-label-color | — | |
--tanrei-comp-file-picker-standard-focused-outline-color | — | |
--tanrei-comp-file-picker-standard-focused-selection-text-color | — | |
--tanrei-comp-file-picker-standard-focused-state-layer-color | — | |
--tanrei-comp-file-picker-standard-focused-supporting-text-color | — | |
--tanrei-comp-file-picker-standard-hovered-container-color | — | |
--tanrei-comp-file-picker-standard-hovered-error-outline-color | — | |
--tanrei-comp-file-picker-standard-hovered-icon-color | — | |
--tanrei-comp-file-picker-standard-hovered-label-color | — | |
--tanrei-comp-file-picker-standard-hovered-outline-color | — | |
--tanrei-comp-file-picker-standard-hovered-selection-text-color | — | |
--tanrei-comp-file-picker-standard-hovered-state-layer-color | — | |
--tanrei-comp-file-picker-standard-hovered-supporting-text-color | — | |
--tanrei-comp-file-picker-standard-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-file-picker-standard-label-color | sys.color.on-surface | |
--tanrei-comp-file-picker-standard-outline-color | sys.color.outline | |
--tanrei-comp-file-picker-standard-pressed-container-color | — | |
--tanrei-comp-file-picker-standard-pressed-error-outline-color | — | |
--tanrei-comp-file-picker-standard-pressed-icon-color | — | |
--tanrei-comp-file-picker-standard-pressed-label-color | — | |
--tanrei-comp-file-picker-standard-pressed-outline-color | — | |
--tanrei-comp-file-picker-standard-pressed-selection-text-color | — | |
--tanrei-comp-file-picker-standard-pressed-state-layer-color | — | |
--tanrei-comp-file-picker-standard-pressed-supporting-text-color | — | |
--tanrei-comp-file-picker-standard-selection-text-color | sys.color.on-surface-variant | |
--tanrei-comp-file-picker-standard-state-layer-color | comp.file-picker.standard.label.color | |
--tanrei-comp-file-picker-standard-supporting-text-color | sys.color.on-surface-variant | |
--tanrei-comp-file-picker-supporting-text-gap | sys.space.space100 | 8px |
--tanrei-comp-file-picker-trigger-gap | sys.space.space150 | 12px |
--tanrei-comp-file-picker-label-typescale | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-file-picker-selection-text-typescale | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-file-picker-supporting-text-typescale | sys.typescale.body-small | 400 13px/1.5385 |
نماذج الاختبار
عروض مرجعية. تلتقط كل المنصات لقطة لكل نموذج بالمعرّف نفسه حتى يمكن مقارنة النتائج.
button
accept: [".pdf"] maxSize: 10000000 label: Attachment constraintsText: PDF, up to 10 MB selectionText: No file chosen slots: trigger button-chosen
accept: [".pdf"] maxSize: 10000000 label: Attachment constraintsText: PDF, up to 10 MB selectionText: brief.pdf slots: trigger button-constraints
multiple: true label: Attachments constraintsText: PDF or PNG, up to 10 MB each slots: trigger button-bare
slots: trigger button-disabled
label: Attachment constraintsText: PDF, up to 10 MB selectionText: No file chosen disabled: true slots: trigger drop-area
layout: drop-area multiple: true accept: [".pdf","image/png"] maxSize: 10000000 label: Attachments constraintsText: PDF or PNG, up to 10 MB each dropText: Drag files here, or choose them slots: trigger drop-area-error
layout: drop-area multiple: true label: Attachments constraintsText: PDF or PNG, up to 10 MB each dropText: Drag files here, or choose them error: true slots: trigger drop-area-disabled
layout: drop-area multiple: true label: Attachments constraintsText: PDF or PNG, up to 10 MB each dropText: Drag files here, or choose them disabled: true slots: trigger drop-area-icon
layout: drop-area accept: ["image/jpeg","image/png"] label: Photo constraintsText: JPEG or PNG, up to 5 MB dropText: Drag a photo here, or choose one slots: trigger, icon drop-area-trigger-only
layout: drop-area multiple: true label: Attachments slots: trigger long-text
layout: drop-area multiple: true label: Attachments constraintsText: PDF, PNG, JPEG, TIFF or a spreadsheet, u… dropText: Drag the signed forms and every supporti… slots: trigger button-long-text
label: Attachment constraintsText: PDF, PNG, JPEG, TIFF or a spreadsheet, u… selectionText: municipal-planning-application-supportin… slots: trigger button-rtl
label: Attachment constraintsText: PDF, up to 10 MB selectionText: brief.pdf direction: rtl slots: trigger drop-area-rtl
layout: drop-area multiple: true label: Attachments constraintsText: PDF or PNG, up to 10 MB each dropText: Drag files here, or choose them direction: rtl slots: trigger مولَّد من specs/file-picker.spec.json, tokens/comp/file-picker.tokens.json, design/tokens/
| التخطيط | يُستخدم لـ | ما يرسمه |
|---|---|---|
button (الافتراضي) |
موضع في نموذج بين حقول أخرى، أو مكان ضيق، أو هاتف، أو شريط أدوات. | المشغِّل (trigger)، والنص الذي بجانبه ويقول ما اختير، والقيود تحته. ولا يقبل الإفلات. |
drop-area |
شاشة مهمتها الرئيسية تسليم ملفات، ويُرجَّح فيها استخدام مؤشر إدخال (pointer). | منطقة مؤطَّرة حول المشغِّل نفسه، فيها رسم أيقونة (glyph) وعبارة إرشادية. تقبل الملفات التي تُسحب إليها، والضغط في أي مكان منها يفتح المنتقي. |
كلاهما يفتح المنتقي نفسه من الزر نفسه. والسحب إضافة، وليس الطريقة الوحيدة أبدًا: فالمشغِّل هو ما تُختار به الملفات بلوحة المفاتيح وباللمس وبالتقنيات المساعدة.
المشغِّل زر من عندك، بالتسمية والطراز والحجم التي تختارها. ضع زرًّا واحدًا لا غير في المنفذ trigger. ومنتقي الملفات يعطيه السلوك ولا يغيّر تنسيقه أبدًا.
- منتقي الملفات الذي بلا مشغِّل يظل يقبل الإفلات في تخطيط منطقة الإفلات، وتبلّغ عنه بنيات التنقيح (debug) على أنه فشل.
- أي شيء آخر تضعه داخله، غير المشغِّل ورسم في المنفذ
icon، لا يُرسم. وتبلّغ عنه بنيات التنقيح. - المنفذ
iconيحلّ محل رسم الرفع في أعلى منطقة الإفلات: رسم صورة للصور، ورسم جدول لجدول بيانات. وهو زخرفي، ويُتجاهل في تخطيط الزر.
الخاصية selectionText هي النص الذي بجانب المشغِّل في تخطيط الزر: «لم يُختر أي ملف»، ثم «brief.pdf»، ثم «3 ملفات». ومنتقي الملفات لا يحتفظ بأي ملف، فلا يستطيع أن يكتب هذا النص. يكتبه من يحتفظ بالملفات: أنت، أو مكوّن رفع الملفات الذي يملؤه عنك.
اتركها فارغة فلا يُرسم شيء هناك، وهذا يناسب زر إرفاق تظهر ملفاته في مكان آخر.
الخصائص accept و maxSize و maxFiles و multiple تفرض القيود. والخاصية constraintsText تقولها. أعطِ الأمرين معًا.
| الخاصية | القيد | الملف الذي يخالفه |
|---|---|---|
accept |
الأصناف المقبولة: امتدادات الملفات بنقطتها (.pdf) وأنواع الوسائط، وعائلة كاملة بالصيغة image/*. |
يُرفض بالسبب type. والمجلد يُرفض بالسبب type أيضًا. |
maxSize |
أكبر حجم لملف واحد، بالبايت. | يُرفض بالسبب size. |
multiple |
هل يجوز أن يأتي الاختيار الواحد بأكثر من ملف. | من دونها تُرفض الملفات التي تصل معًا كلها، بالسبب count. ومنتقي الملفات لا يختار واحدًا منها نيابة عن الشخص. |
maxFiles |
أكثر ما يأتي به اختيار واحد أو إفلات واحد من ملفات، مع multiple. |
الملفات الزائدة عليه تُرفض بالسبب count. والصفر يرفض كل ملف. |
- اكتب القيود كما يقرؤها الشخص: «ملفات PDF أو PNG، حتى 10 ميغابايت لكل ملف». لا يبني Tanrei جملة من
acceptولا من عدد بايتات، لأنه لا يتضمن أي كلمة. - القيود تُرسم في منتقي الملفات لا في مساعدة الحقل. فمساعدة الحقل تخلي مكانها لنص الخطأ، وتلك بالضبط هي اللحظة التي يحتاج فيها الشخص إلى قراءة القيود من جديد.
- يصدر الحدث
selectمرة واحدة لكل اختيار ومعه الملفات التي قُبلت، ثم الحدثrejectمرة واحدة ومعه كل ملف مرفوض وسببه. ومنتقي الملفات لا يرسم شيئًا ولا يقول شيئًا عند الرفض. قل ما حدث بكلماتك: بنص الخطأ في حقل، أو بصف في قائمتك، أو برسالة تعلنها. - القيود قيود اختيار واحد. فمنتقي الملفات لا يعرف ما احتفظت به من قبل: عيِّن
maxFilesعلى عدد الملفات التي ما زال لها مكان، أو احكم على المجموع بنفسك. ومكوّن رفع الملفات يفعل ذلك. - ما دام شيء يُسحب فوق المنطقة، لا يخبر المتصفح إلا بالأصناف والعدد، ولا يخبر بالأسماء ولا بالأحجام أبدًا. ولذلك يُرفض الملف الأكبر من الحد عند الإفلات، لا وهو ما زال مسحوبًا.
ضع منتقي الملفات داخل حقل حين يعرض النموذج تسمية أو مساعدة أو خطأ أو علامة المطلوب. فالحقل يرسمها ويعطيها لمنتقي الملفات، وعندئذ تسمّي مشغِّله وتصفه.
<tanrei-field label="المرفق" required required-text="مطلوب"
error error-text="اختر ملف PDF لا يتجاوز حجمه 10 ميغابايت.">
<tanrei-file-picker accept=".pdf" max-size="10000000"
constraints-text="ملف PDF، حتى 10 ميغابايت" selection-text="لم يُختر أي ملف">
<tanrei-button slot="trigger" variant="outlined">اختيار ملف</tanrei-button>
</tanrei-file-picker>
</tanrei-field>منتقي الملفات لا يرسل شيئًا. فليس له name ولا قيمة، والخاصية required تخبر التقنيات المساعدة فقط. أما القول إن ملفًا ناقص فشأن المكوّن الذي يحتفظ بالملفات: شيفرتك، أو مكوّن رفع الملفات، وهو عنصر تحكم في النماذج. والقيمة required، كما يقولها الحقل، تساوي required.
في تخطيط من اليمين إلى اليسار يقف المشغِّل عند اليمين ونص ما اختير عن يساره، والقيود تبدأ من الحافة اليمنى. وعمود منطقة الإفلات يبقى في الوسط. واسم الملف الذي يُعطى في selectionText يحتفظ بترتيبه، بنقطته وامتداده. والرسم لا يُعكس إلا حيث تعلّمه مجموعة الأيقونات بذلك.
- احتفظ بما يعطيك إياه الحدث
select، في الحال. - قل ما الذي اختير: بالخاصية
selectionText، أو في المكان الذي يعرض فيه منتجك الملفات. - قل كل رفض بالكلمات، حيث ينظر الشخص.
- استخدم مكوّن رفع الملفات حين تُسرد الملفات تحت المنتقي وتُرسل مع نموذج.
- لا تجعل منطقة الإفلات الطريقة الوحيدة للاختيار. ففيها المشغِّل دائمًا.
- لا تنتظر من نموذج أن يرسل ما اختير. فمنتقي الملفات لا يشارك في أي نموذج.
- لا تغيّر تنسيق المشغِّل لهذا الموضع وحده بالوصول إلى داخله. فهو زرك: اختر طرازه وحجمه.
- لا تبنِ قائمة ملفات داخله. ضع عناصر الملفات تحته، أو استخدم مكوّن رفع الملفات.
- الدور
group- التسمية
- A group named by label, or by the label of the Field around it; with neither, no group — the trigger alone, a button named by its own label. The trigger is described by constraintsText, by selectionText while it is drawn, and by what the Field or the component that holds the file picker gives; it is exposed as required, invalid and disabled as the props say. The drop area and its glyph are not exposed; dropText is read as text.
- الحد الأدنى للهدف
- تستوفي مساحة اللمس الحد الأدنى لحجم الهدف حتى لو كان الحجم المرئي أصغر. minimum 32px · pointer 44px · spacing 8px · touch 48px
لوحة المفاتيح
| المفاتيح | الإجراء |
|---|---|
| Tab / Shift+Tab | Moves focus to the trigger. The drop area is not a stop. |
| Enter / Space | On the trigger: opens the platform's file picker, as the Button's own activation. |
| The picker's own keys | Inside the platform's picker everything is the platform's; leaving it without choosing returns focus to the trigger and changes nothing. |
ملاحظات
- Dragging is never the only way (WCAG 2.5.7): the trigger opens the same picker in both layouts and is the path for keyboard, touch, switch and voice. A drop area without a trigger is reported as a functional failure (FPK-5). The area adds a larger pointer target and takes drops; it adds no role, no stop and no key (FPK-12).
- Without script, and until the component is defined, on the web: the trigger is a button that opens nothing. A custom element cannot open the platform's picker by markup alone, and the file picker does not pretend to: a page that must take files where script does not run writes a native file input in its own form, as it would without Tanrei, and loses only the drawing.
- The limits are words before the choice (WCAG 3.3.2): constraintsText is drawn and is the trigger's description. accept, maxSize and maxFiles enforce what it says; a product that sets them and writes no words is told so in debug builds (FPK-18).
- A refused file must be said (WCAG 3.3.1), and only the product has the words and the place. On reject the product sets the errorText of the Field around the file picker, marks the file in a list of its own, or announces a sentence through a live region of its own. The file picker draws and says nothing for a refusal by itself, because it has no sentence for it and no row to put one on; the component built from it that keeps files draws such a row.
- What is dragged over the area is the pointer user's own act and is not announced. The conditions are told without color too: receiving by its fill and, under forced colors, a thicker solid edge; refusing by the block glyph (FPK-13, FPK-23).
- The file picker has no live region (FPK-17): it holds nothing whose change it could say. What was chosen is said by whoever shows it — the text beside the trigger, which is part of the trigger's description, or a list the product draws.
- A file picker alone is no form control (FPK-7). It keeps nothing, so a value it submitted would be a file nobody can see or take back, and a required check would ask for something it cannot hold. A plain form takes the component that keeps the files and lists them; a product that draws its own list holds the files and sends them itself, or gives them to a control of its own.
- required is a state for assistive technology alone, and a button has none on the web: there it is said by the Field's required text in the trigger's description, as for every control whose role has no required state.
مولَّد من specs/file-picker.spec.json, tokens/sys/measure.tokens.json
- المشغِّل زرك. تصفه القيود والنص الذي يقول ما اختير، وداخل الحقل تصفه كذلك مساعدة الحقل أو خطؤه.
- مع
label، أو داخل حقل، يكون منتقي الملفات مجموعة بذلك الاسم. ومن دون اسم لا يعرض للتقنيات المساعدة أي مجموعة: فزر الإرفاق في شريط أدوات زر لا غير. - منطقة الإفلات هدف مؤشر واحد كبير لذلك الزر نفسه. ليس لها دور ولا اسم ولا موضع توقف في ترتيب التركيز ولا مفتاح.
- الخاصية
errorتعرض المشغِّل للتقنيات المساعدة على أنه غير صالح وترسم حافة المنطقة بلون الخطأ. والخاصيةdisabledتعطّل المشغِّل، والمنطقة لا تستجيب لشيء. - منتقي الملفات لا يعلن شيئًا. فليست له منطقة حيّة (live region): ما اختير أو رُفض يقوله من يحتفظ بالملفات.
- عند تكبير النص إلى 200% يلتفّ كل نص ولا يُقصّ شيء. وفي الألوان المفروضة (forced colors) تحتفظ المنطقة بحافة، وللمنطقة التي تستقبل سحبًا حافة أثخن.
كيف بُني على الويب: المنتقي عنصر <input type="file"> حقيقي في جذر الظل (shadow root)، محجوب عن النظر وعن ترتيب التركيز وعن التقنيات المساعدة. والمشغِّل والرسم يبقيان في شجرة DOM الخاصة بك. والحدثان select و reject ينتقلان صعودًا في الشجرة؛ والقيمة detail.files مصفوفة File[]، وهي في reject قائمة من { file, reason }.
فجوات معروفة على الويب:
- ليس للزر حالة «مطلوب». داخل الحقل ينضم نص المطلوب الخاص بالحقل إلى وصف المشغِّل.
- المتصفح لا يسلّم أي إفلات إلى منطقة ترفض السحب. فلا يصدر الحدث
rejectعندئذ إلا لما لم يكن ممكنًا أن يُعرف قبل الإفلات، كملف أكبر من الحد. - في Safari لا تعطي النقرة الزر تركيزًا. فبعد أن يُختار ملف هناك بمؤشر، يكون التركيز حيث كان قبل ذلك.
- الحافة المتقطعة لا تستطيع أن تتبع الزوايا المنعَّمة. ففي نظام تصميم يعيّن تنعيم الزوايا تُرسم الحافة متصلة حيث يرسم المتصفح ذلك المنحنى.
- من دون سكربت، وإلى أن يُعرَّف العنصر، يكون المشغِّل زرًّا لا يفتح شيئًا. والصفحة التي يجب أن تقبل الملفات حيث لا يعمل السكربت تكتب عنصر
<input type="file">أصليًّا.
- ضع زرًّا واحدًا في المنفذ
trigger، بتسمية تقول ما يفعله. - أعطِ منتقي الملفات
label، أو ضعه داخل حقل، حيثما كان أكثر من زر منفرد. - أعطِ
constraintsTextكلما عيّنتacceptأوmaxSizeأوmaxFiles. - قل ما الذي اختير، وقل كل رفض، بالكلمات.
- أعلن ما أُضيف في منطقة حيّة من عندك حين تظهر الملفات في مكان لا ينظر إليه الشخص.