feat: display file chosen for input file (#35789)

Signed-off-by: DK Liao <dklassic@gmail.com>
This commit is contained in:
DK Liao 2025-03-10 12:55:38 +09:00 committed by GitHub
parent 34047f8da8
commit ce4ba30992
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 5 deletions

View file

@ -6,6 +6,7 @@ use std::slice::Iter;
use dom_struct::dom_struct;
use super::bindings::root::{LayoutDom, ToLayout};
use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
@ -69,3 +70,23 @@ impl FileListMethods<crate::DomTypeHolder> for FileList {
self.Item(index)
}
}
pub(crate) trait LayoutFileListHelpers<'dom> {
fn file_for_layout(&self, index: u32) -> Option<&File>;
fn len(&self) -> usize;
}
#[allow(unsafe_code)]
impl<'dom> LayoutFileListHelpers<'dom> for LayoutDom<'dom, FileList> {
fn len(&self) -> usize {
self.unsafe_get().list.len()
}
fn file_for_layout(&self, index: u32) -> Option<&File> {
let list = &self.unsafe_get().list;
if (index as usize) < list.len() {
Some(unsafe { list[index as usize].to_layout().unsafe_get() })
} else {
None
}
}
}

View file

@ -57,7 +57,7 @@ use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File;
use crate::dom::filelist::FileList;
use crate::dom::filelist::{FileList, LayoutFileListHelpers};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmldatalistelement::HTMLDataListElement;
use crate::dom::htmlelement::HTMLElement;
@ -90,6 +90,7 @@ use crate::textinput::{
const DEFAULT_SUBMIT_VALUE: &str = "Submit";
const DEFAULT_RESET_VALUE: &str = "Reset";
const PASSWORD_REPLACEMENT_CHAR: char = '●';
const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen";
/// <https://html.spec.whatwg.org/multipage/#attr-input-type>
#[derive(Clone, Copy, Default, JSTraceable, PartialEq)]
@ -1037,6 +1038,9 @@ impl<'dom> LayoutDom<'dom, HTMLInputElement> {
.get_content()
}
}
fn get_filelist(self) -> Option<LayoutDom<'dom, FileList>> {
unsafe { self.unsafe_get().filelist.get_inner_as_layout() }
}
fn placeholder(self) -> &'dom str {
unsafe { self.unsafe_get().placeholder.borrow_for_layout() }
@ -1070,8 +1074,27 @@ impl<'dom> LayoutHTMLInputElementHelpers<'dom> for LayoutDom<'dom, HTMLInputElem
}
match self.input_type() {
InputType::Checkbox | InputType::Radio => "".into(),
InputType::File | InputType::Image => "".into(),
InputType::Checkbox | InputType::Radio | InputType::Image => "".into(),
InputType::File => {
let filelist = self.get_filelist();
match filelist {
Some(filelist) => {
let length = filelist.len();
if length == 0 {
return DEFAULT_FILE_INPUT_VALUE.into();
}
if length == 1 {
match filelist.file_for_layout(0) {
Some(file) => return file.name().to_string().into(),
None => return DEFAULT_FILE_INPUT_VALUE.into(),
}
}
format!("{} files", length).into()
},
None => DEFAULT_FILE_INPUT_VALUE.into(),
}
},
InputType::Button => get_raw_attr_value(self, ""),
InputType::Submit => get_raw_attr_value(self, DEFAULT_SUBMIT_VALUE),
InputType::Reset => get_raw_attr_value(self, DEFAULT_RESET_VALUE),

View file

@ -74,13 +74,17 @@ input[type="radio"]:checked::before { content: "●"; line-height: 1em; }
input[type="file"]::before {
content: "Choose File";
background: lightgrey;
border-top: solid 1px #EEEEEE;
border-left: solid 1px #CCCCCC;
border-right: solid 1px #999999;
border-bottom: solid 1px #999999;
}
input[type="file"] {
background: lightgrey;
text-align: center;
vertical-align: middle;
color: black;
border-style: none;
}
select {