Dialogs, a11y · Dialog card names its role, title and detail; prompt error reads as a label; Cancel and Close read i18n; a disabled button says so in its description
This commit is contained in:
6 files changed
+39
-11
No files matched your search
@@ -5,6 +5,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
i18n,
|
||||||
motion::Spinner,
|
motion::Spinner,
|
||||||
primitives::{FocusRing, Icon, IconName},
|
primitives::{FocusRing, Icon, IconName},
|
||||||
theme::{ActiveTheme, ControlSize, IconSize, Mix, Palette, Platform, Radius, TextSize},
|
theme::{ActiveTheme, ControlSize, IconSize, Mix, Palette, Platform, Radius, TextSize},
|
||||||
@@ -291,7 +292,10 @@ impl RenderOnce for Button {
|
|||||||
})
|
})
|
||||||
.map(|el| {
|
.map(|el| {
|
||||||
if self.disabled {
|
if self.disabled {
|
||||||
return el.opacity(0.45).cursor_not_allowed();
|
return el
|
||||||
|
.aria_description(i18n::text(cx, "state.unavailable", &[]))
|
||||||
|
.opacity(0.45)
|
||||||
|
.cursor_not_allowed();
|
||||||
}
|
}
|
||||||
let el = match &self.focus {
|
let el = match &self.focus {
|
||||||
Some(handle) => el.track_focus(handle),
|
Some(handle) => el.track_focus(handle),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use gpui::{
|
|||||||
|
|
||||||
use super::button::{ButtonVariant, ClickHandler, label_size, tone};
|
use super::button::{ButtonVariant, ClickHandler, label_size, tone};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
i18n,
|
||||||
primitives::{FocusRing, Icon, IconName, Tooltip},
|
primitives::{FocusRing, Icon, IconName, Tooltip},
|
||||||
theme::{ActiveTheme, ControlSize, Radius},
|
theme::{ActiveTheme, ControlSize, Radius},
|
||||||
};
|
};
|
||||||
@@ -92,7 +93,10 @@ impl RenderOnce for IconButton {
|
|||||||
.when_some(self.tooltip, |el, text| el.tooltip(Tooltip::text(text)))
|
.when_some(self.tooltip, |el, text| el.tooltip(Tooltip::text(text)))
|
||||||
.map(|el| {
|
.map(|el| {
|
||||||
if self.disabled {
|
if self.disabled {
|
||||||
return el.opacity(0.45).cursor_not_allowed();
|
return el
|
||||||
|
.aria_description(i18n::text(cx, "state.unavailable", &[]))
|
||||||
|
.opacity(0.45)
|
||||||
|
.cursor_not_allowed();
|
||||||
}
|
}
|
||||||
el.cursor_pointer()
|
el.cursor_pointer()
|
||||||
.tab_index(0)
|
.tab_index(0)
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ const EN: &[(&str, &str)] = &[
|
|||||||
("palette.branch.delete", "Delete"),
|
("palette.branch.delete", "Delete"),
|
||||||
("palette.history.placeholder", "Search past commands"),
|
("palette.history.placeholder", "Search past commands"),
|
||||||
("palette.history.empty", "No past command fits"),
|
("palette.history.empty", "No past command fits"),
|
||||||
|
("dialog.cancel", "Cancel"),
|
||||||
|
("dialog.close", "Close"),
|
||||||
|
("state.unavailable", "Unavailable"),
|
||||||
];
|
];
|
||||||
|
|
||||||
const ZH_CN: &[(&str, &str)] = &[
|
const ZH_CN: &[(&str, &str)] = &[
|
||||||
@@ -62,6 +65,9 @@ const ZH_CN: &[(&str, &str)] = &[
|
|||||||
("palette.branch.delete", "删除"),
|
("palette.branch.delete", "删除"),
|
||||||
("palette.history.placeholder", "搜索历史命令"),
|
("palette.history.placeholder", "搜索历史命令"),
|
||||||
("palette.history.empty", "没有匹配的历史命令"),
|
("palette.history.empty", "没有匹配的历史命令"),
|
||||||
|
("dialog.cancel", "取消"),
|
||||||
|
("dialog.close", "关闭"),
|
||||||
|
("state.unavailable", "不可用"),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Ely's own message for `key` in `locale`, English for a locale Ely does not ship; fails on an unknown key.
|
/// Ely's own message for `key` in `locale`, English for a locale Ely does not ship; fails on an unknown key.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use super::TabOverflowMenu;
|
|||||||
use crate::{
|
use crate::{
|
||||||
buttons::{ButtonVariant, IconButton},
|
buttons::{ButtonVariant, IconButton},
|
||||||
forms::Choice,
|
forms::Choice,
|
||||||
|
i18n,
|
||||||
primitives::{DragGhost, Icon, IconName},
|
primitives::{DragGhost, Icon, IconName},
|
||||||
theme::{ActiveTheme, ControlSize, IconSize, TextSize},
|
theme::{ActiveTheme, ControlSize, IconSize, TextSize},
|
||||||
};
|
};
|
||||||
@@ -206,7 +207,7 @@ impl RenderOnce for EditorTabs {
|
|||||||
IconButton::new(("close", ix), IconName::X)
|
IconButton::new(("close", ix), IconName::X)
|
||||||
.size(ControlSize::Sm)
|
.size(ControlSize::Sm)
|
||||||
.variant(ButtonVariant::Ghost)
|
.variant(ButtonVariant::Ghost)
|
||||||
.tooltip("Close")
|
.tooltip(i18n::text(cx, "dialog.close", &[]))
|
||||||
.on_click(move |_, window, cx| {
|
.on_click(move |_, window, cx| {
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
run(&close, "close", &slot_id, window, cx);
|
run(&close, "close", &slot_id, window, cx);
|
||||||
|
|||||||
+11
-4
@@ -2,15 +2,15 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Animation, AnimationExt, AnyElement, App, ElementId, FocusHandle, FontWeight,
|
Animation, AnimationExt, AnyElement, App, ElementId, FocusHandle, FontWeight,
|
||||||
InteractiveElement, IntoElement, ParentElement, Pixels, RenderOnce, SharedString, Styled,
|
InteractiveElement, IntoElement, ParentElement, Pixels, RenderOnce, Role, SharedString,
|
||||||
Window, anchored, div, point, prelude::*,
|
StatefulInteractiveElement, Styled, Window, anchored, div, point, prelude::*,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
buttons::{ButtonVariant, IconButton},
|
buttons::{ButtonVariant, IconButton},
|
||||||
forms::{Enter, Run},
|
forms::{Enter, Run},
|
||||||
motion,
|
i18n, motion,
|
||||||
primitives::{Backdrop, FocusScope, IconName, give_back, raise, take_focus},
|
primitives::{Backdrop, FocusScope, IconName, give_back, raise, take_focus},
|
||||||
theme::{ActiveTheme, Elevation, Radius, TextSize},
|
theme::{ActiveTheme, Elevation, Radius, TextSize},
|
||||||
};
|
};
|
||||||
@@ -119,6 +119,7 @@ impl RenderOnce for Dialog {
|
|||||||
};
|
};
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let colors = &theme.colors;
|
let colors = &theme.colors;
|
||||||
|
let (title, description) = (self.title.clone(), self.detail.clone());
|
||||||
let heading = div()
|
let heading = div()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -164,6 +165,9 @@ impl RenderOnce for Dialog {
|
|||||||
let card = if fullscreen {
|
let card = if fullscreen {
|
||||||
div()
|
div()
|
||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
|
.role(Role::Dialog)
|
||||||
|
.aria_label(title)
|
||||||
|
.when_some(description, |card, text| card.aria_description(text))
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
@@ -182,7 +186,7 @@ impl RenderOnce for Dialog {
|
|||||||
.child(
|
.child(
|
||||||
IconButton::new((self.id.clone(), "close"), IconName::X)
|
IconButton::new((self.id.clone(), "close"), IconName::X)
|
||||||
.variant(ButtonVariant::Ghost)
|
.variant(ButtonVariant::Ghost)
|
||||||
.tooltip("Close")
|
.tooltip(i18n::text(cx, "dialog.close", &[]))
|
||||||
.on_click(move |_, window, cx| button(window, cx)),
|
.on_click(move |_, window, cx| button(window, cx)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -214,6 +218,9 @@ impl RenderOnce for Dialog {
|
|||||||
});
|
});
|
||||||
div()
|
div()
|
||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
|
.role(Role::Dialog)
|
||||||
|
.aria_label(title)
|
||||||
|
.when_some(description, |card, text| card.aria_description(text))
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.gap_5()
|
.gap_5()
|
||||||
|
|||||||
+10
-4
@@ -1,14 +1,15 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, ElementId, Entity, IntoElement, ParentElement, RenderOnce, SharedString, Styled, Window,
|
App, ElementId, Entity, IntoElement, ParentElement, RenderOnce, Role, SharedString,
|
||||||
div, prelude::*,
|
StatefulInteractiveElement, Styled, Window, div, prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Close, Dialog};
|
use super::{Close, Dialog};
|
||||||
use crate::{
|
use crate::{
|
||||||
buttons::{Button, ButtonVariant},
|
buttons::{Button, ButtonVariant},
|
||||||
forms::{Input, Run, TextInput},
|
forms::{Input, Run, TextInput},
|
||||||
|
i18n,
|
||||||
primitives::{Icon, Severity},
|
primitives::{Icon, Severity},
|
||||||
theme::{ActiveTheme, ControlSize, IconSize, TextSize},
|
theme::{ActiveTheme, ControlSize, IconSize, TextSize},
|
||||||
};
|
};
|
||||||
@@ -156,10 +157,11 @@ impl RenderOnce for ConfirmDialog {
|
|||||||
};
|
};
|
||||||
let (cancel_id, confirm_id) = ((self.id.clone(), "cancel"), (self.id.clone(), "confirm"));
|
let (cancel_id, confirm_id) = ((self.id.clone(), "cancel"), (self.id.clone(), "confirm"));
|
||||||
let on_close = self.on_close;
|
let on_close = self.on_close;
|
||||||
|
let cancel = i18n::text(cx, "dialog.cancel", &[]);
|
||||||
Dialog::new(self.id, self.title, move |window, cx| on_close(window, cx))
|
Dialog::new(self.id, self.title, move |window, cx| on_close(window, cx))
|
||||||
.child(said(None, self.message, cx))
|
.child(said(None, self.message, cx))
|
||||||
.action(move |close| {
|
.action(move |close| {
|
||||||
Button::new(cancel_id, "Cancel")
|
Button::new(cancel_id, cancel)
|
||||||
.variant(ButtonVariant::Ghost)
|
.variant(ButtonVariant::Ghost)
|
||||||
.on_click(move |_, window, cx| close(window, cx))
|
.on_click(move |_, window, cx| close(window, cx))
|
||||||
})
|
})
|
||||||
@@ -295,6 +297,9 @@ impl RenderOnce for PromptDialog {
|
|||||||
|field, reason| {
|
|field, reason| {
|
||||||
field.child(
|
field.child(
|
||||||
div()
|
div()
|
||||||
|
.id((self.id.clone(), "error"))
|
||||||
|
.role(Role::Label)
|
||||||
|
.aria_label(reason.clone())
|
||||||
.text_size(theme.text_size(TextSize::Xs))
|
.text_size(theme.text_size(TextSize::Xs))
|
||||||
.text_color(colors.danger)
|
.text_color(colors.danger)
|
||||||
.child(reason),
|
.child(reason),
|
||||||
@@ -306,13 +311,14 @@ impl RenderOnce for PromptDialog {
|
|||||||
(self.id.clone(), "submit"),
|
(self.id.clone(), "submit"),
|
||||||
self.submit,
|
self.submit,
|
||||||
);
|
);
|
||||||
|
let cancel = i18n::text(cx, "dialog.cancel", &[]);
|
||||||
let on_close = self.on_close;
|
let on_close = self.on_close;
|
||||||
Dialog::new(self.id, self.title, move |window, cx| on_close(window, cx))
|
Dialog::new(self.id, self.title, move |window, cx| on_close(window, cx))
|
||||||
.focus_first(self.field.read(cx).focus().clone())
|
.focus_first(self.field.read(cx).focus().clone())
|
||||||
.on_enter(move |close| Rc::new(move |window, cx| lifted(&close, window, cx)))
|
.on_enter(move |close| Rc::new(move |window, cx| lifted(&close, window, cx)))
|
||||||
.child(field)
|
.child(field)
|
||||||
.action(move |close| {
|
.action(move |close| {
|
||||||
Button::new(cancel_id, "Cancel")
|
Button::new(cancel_id, cancel)
|
||||||
.variant(ButtonVariant::Ghost)
|
.variant(ButtonVariant::Ghost)
|
||||||
.on_click(move |_, window, cx| close(window, cx))
|
.on_click(move |_, window, cx| close(window, cx))
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user