Insert table
This commit is contained in:
parent
4b436e953e
commit
693ade0de1
@ -55,6 +55,7 @@ features = [
|
||||
"FormData",
|
||||
"FileReader",
|
||||
"FileReaderSync",
|
||||
"Range",
|
||||
# events
|
||||
"EventTarget",
|
||||
"ErrorEvent",
|
||||
|
@ -64,12 +64,29 @@
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.styledRte > .editorWrapper > .editor table {
|
||||
table-layout: fixed;
|
||||
border-spacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.styledRte > .editorWrapper > .editor table > tbody > tr > td {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid var(--borderLight);
|
||||
border-right: 1px solid var(--borderLight);
|
||||
border-left: 1px solid var(--borderLight);
|
||||
border-top: 1px solid var(--borderLight);
|
||||
}
|
||||
|
||||
/**********************************************************/
|
||||
/* Table tooltip */
|
||||
/**********************************************************/
|
||||
|
||||
.tableTooltip {
|
||||
min-width: 120px;
|
||||
min-width: 136px;
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
}
|
||||
@ -80,13 +97,19 @@
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.tableTooltip > table {
|
||||
.tableTooltip > .tablePreview {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tableTooltip > .tablePreview > table {
|
||||
table-layout: fixed;
|
||||
border-spacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tableTooltip > table > tbody > tr > td {
|
||||
.tableTooltip > .tablePreview > table > tbody > tr > td {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
text-align: center;
|
||||
@ -97,6 +120,12 @@
|
||||
border-top: 1px solid var(--borderLight);
|
||||
}
|
||||
|
||||
.tableTooltip > .tablePreview > input {
|
||||
height: 32px;
|
||||
align-self: flex-end;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.tableTooltip > .inputs {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -32,8 +32,13 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
|
||||
let description_field = description_field(page);
|
||||
|
||||
let desc_rte = StyledRte::build(FieldId::ProjectSettings(ProjectFieldId::Description))
|
||||
.state(&page.description_rte)
|
||||
let desc_rte = StyledField::build()
|
||||
.input(
|
||||
StyledRte::build(FieldId::ProjectSettings(ProjectFieldId::Description))
|
||||
.state(&page.description_rte)
|
||||
.build()
|
||||
.into_node(),
|
||||
)
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
|
@ -55,6 +55,7 @@ pub enum RteMsg {
|
||||
TableSetRows(u16),
|
||||
TableSetColumns(u16),
|
||||
TableSetVisibility(bool),
|
||||
InsertTable { rows: u16, cols: u16 },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -108,6 +109,7 @@ impl RteMsg {
|
||||
RteMsg::RemoveFormat => Some(ExecCommand::new("removeFormat")),
|
||||
RteMsg::Subscript => Some(ExecCommand::new("subscript")),
|
||||
RteMsg::Superscript => Some(ExecCommand::new("superscript")),
|
||||
RteMsg::InsertTable { .. } => None,
|
||||
// outer
|
||||
RteMsg::TableSetColumns(..)
|
||||
| RteMsg::TableSetRows(..)
|
||||
@ -128,6 +130,7 @@ pub struct StyledRteState {
|
||||
pub value: String,
|
||||
pub field_id: FieldId,
|
||||
pub table_tooltip: StyledRteTableState,
|
||||
range: Option<web_sys::Range>,
|
||||
}
|
||||
|
||||
impl StyledRteState {
|
||||
@ -140,6 +143,7 @@ impl StyledRteState {
|
||||
rows: 3,
|
||||
cols: 3,
|
||||
},
|
||||
range: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +154,7 @@ impl StyledRteState {
|
||||
};
|
||||
match m.to_command() {
|
||||
Some(ExecCommand { name, param }) => {
|
||||
self.store_range();
|
||||
match seed::html_document().exec_command_with_show_ui_and_value(
|
||||
name.as_str(),
|
||||
false,
|
||||
@ -158,6 +163,9 @@ impl StyledRteState {
|
||||
Ok(_) => {}
|
||||
Err(e) => log!(e),
|
||||
}
|
||||
if self.restore_range().is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => match m {
|
||||
RteMsg::TableSetRows(n) => {
|
||||
@ -167,12 +175,70 @@ impl StyledRteState {
|
||||
self.table_tooltip.cols = *n;
|
||||
}
|
||||
RteMsg::TableSetVisibility(b) => {
|
||||
if *b {
|
||||
self.store_range();
|
||||
}
|
||||
self.table_tooltip.visible = *b;
|
||||
}
|
||||
RteMsg::InsertTable { rows, cols } => {
|
||||
self.table_tooltip.visible = false;
|
||||
self.table_tooltip.cols = 3;
|
||||
self.table_tooltip.rows = 3;
|
||||
if self.restore_range().is_err() {
|
||||
return;
|
||||
}
|
||||
let doc = seed::html_document();
|
||||
let r = match self.range.as_ref() {
|
||||
Some(r) => r,
|
||||
_ => return,
|
||||
};
|
||||
let table = match doc.create_element("table") {
|
||||
Ok(t) => t,
|
||||
_ => return,
|
||||
};
|
||||
let mut buff = "<tbody>".to_string();
|
||||
for _c in 0..(*cols) {
|
||||
buff.push_str("<tr>");
|
||||
for _r in 0..(*rows) {
|
||||
buff.push_str("<td> </td>")
|
||||
}
|
||||
buff.push_str("</tr>");
|
||||
}
|
||||
buff.push_str("</tbody>");
|
||||
table.set_inner_html(buff.as_str());
|
||||
r.insert_node(&table);
|
||||
}
|
||||
_ => log!(m),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn store_range(&mut self) {
|
||||
self.range = seed::html_document()
|
||||
.get_selection()
|
||||
.ok()
|
||||
.unwrap_or_else(|| None)
|
||||
.and_then(|s| s.get_range_at(0).ok());
|
||||
}
|
||||
|
||||
fn restore_range(&mut self) -> Result<(), String> {
|
||||
let doc = seed::html_document();
|
||||
let sel = doc
|
||||
.get_selection()
|
||||
.ok()
|
||||
.unwrap_or_else(|| None)
|
||||
.ok_or_else(|| "Restoring selection failed. Unable to obtain select".to_string())?;
|
||||
let r = self
|
||||
.range
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Restoring selection failed. No range was stored".to_string())?;
|
||||
sel.remove_all_ranges()
|
||||
.map_err(|_| "Restoring selection failed. Unable to remove ranges".to_string())?;
|
||||
sel.add_range(r).map_err(|_| {
|
||||
"Restoring selection failed. Unable to add current selection range".to_string()
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StyledRte {
|
||||
@ -301,8 +367,15 @@ pub fn render(values: StyledRte) -> Node<Msg> {
|
||||
);
|
||||
}
|
||||
|
||||
let capture_event = ev(Ev::KeyDown, |ev| {
|
||||
ev.stop_propagation();
|
||||
None as Option<Msg>
|
||||
});
|
||||
let id = values.field_id.to_string();
|
||||
|
||||
div![
|
||||
class!["styledRte"],
|
||||
attrs![At::Id => id],
|
||||
div![
|
||||
class!["bar"],
|
||||
first_row(&values),
|
||||
@ -318,7 +391,11 @@ pub fn render(values: StyledRte) -> Node<Msg> {
|
||||
],
|
||||
div![
|
||||
class!["editorWrapper"],
|
||||
div![class!["editor"], attrs![At::ContentEditable => true]],
|
||||
div![
|
||||
class!["editor"],
|
||||
attrs![At::ContentEditable => true],
|
||||
capture_event
|
||||
],
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -579,7 +656,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
||||
.collect();
|
||||
let heading_button = span![class!["headingList"], options];
|
||||
|
||||
let _field_id = values.field_id.clone();
|
||||
/*let _field_id = values.field_id.clone();
|
||||
let _small_cap_button = styled_rte_button(
|
||||
"Small Cap",
|
||||
Icon::SmallCap,
|
||||
@ -596,7 +673,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
||||
ev.prevent_default();
|
||||
None as Option<Msg>
|
||||
}),
|
||||
);
|
||||
);*/
|
||||
div![
|
||||
class!["group font"],
|
||||
// font_button,
|
||||
@ -607,60 +684,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
||||
};
|
||||
|
||||
let insert_group = {
|
||||
let table_tooltip = {
|
||||
let StyledRteTableState {
|
||||
visible,
|
||||
rows,
|
||||
cols,
|
||||
} = values.table_tooltip;
|
||||
let field_id = values.field_id.clone();
|
||||
let on_rows_change = input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
||||
});
|
||||
let field_id = values.field_id.clone();
|
||||
let on_cols_change = input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
||||
});
|
||||
let field_id = values.field_id.clone();
|
||||
let close_table_tooltip = StyledButton::build()
|
||||
.empty()
|
||||
.icon(Icon::Close)
|
||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::TableSetVisibility(false), field_id))
|
||||
}))
|
||||
.build()
|
||||
.into_node();
|
||||
StyledTooltip::build()
|
||||
.table_tooltip()
|
||||
.visible(visible)
|
||||
.add_child(h2![span!["Add table"], close_table_tooltip])
|
||||
.add_child(div![class!["inputs"], span!["Rows"], seed::input![
|
||||
attrs![At::Type => "range"; At::Step => "1"; At::Min => "1"; At::Max => "10"; At::Value => rows],
|
||||
on_rows_change
|
||||
]])
|
||||
.add_child(div![class!["inputs"], span!["Columns"], seed::input![
|
||||
attrs![At::Type => "range"; At::Step => "1"; At::Min => "1"; At::Max => "10"; At::Value => cols],
|
||||
on_cols_change
|
||||
]])
|
||||
.add_child({
|
||||
let body: Vec<Node<Msg>> = (0..rows)
|
||||
.map(|_row| {
|
||||
let tds: Vec<Node<Msg>> = (0..cols)
|
||||
.map(|_col| td![" "])
|
||||
.collect();
|
||||
tr![tds]
|
||||
})
|
||||
.collect();
|
||||
seed::table![tbody![body]]
|
||||
})
|
||||
.build()
|
||||
.into_node()
|
||||
};
|
||||
let table_tooltip = table_tooltip(values);
|
||||
|
||||
let field_id = values.field_id.clone();
|
||||
let listing_dots = styled_rte_button(
|
||||
@ -759,6 +783,74 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
||||
]
|
||||
}
|
||||
|
||||
fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
||||
let StyledRteTableState {
|
||||
visible,
|
||||
rows,
|
||||
cols,
|
||||
} = values.table_tooltip;
|
||||
let field_id = values.field_id.clone();
|
||||
let on_rows_change = input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
||||
});
|
||||
let field_id = values.field_id.clone();
|
||||
let on_cols_change = input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
||||
});
|
||||
let field_id = values.field_id.clone();
|
||||
let close_table_tooltip = StyledButton::build()
|
||||
.empty()
|
||||
.icon(Icon::Close)
|
||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::TableSetVisibility(false), field_id))
|
||||
}))
|
||||
.build()
|
||||
.into_node();
|
||||
let field_id = values.field_id.clone();
|
||||
let on_submit = mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::InsertTable { rows, cols }, field_id))
|
||||
});
|
||||
StyledTooltip::build()
|
||||
.table_tooltip()
|
||||
.visible(visible)
|
||||
.add_child(h2![span!["Add table"], close_table_tooltip])
|
||||
.add_child(div![class!["inputs"], span!["Rows"], seed::input![
|
||||
attrs![At::Type => "range"; At::Step => "1"; At::Min => "1"; At::Max => "10"; At::Value => rows],
|
||||
on_rows_change
|
||||
]])
|
||||
.add_child(div![
|
||||
class!["inputs"],
|
||||
span!["Columns"],
|
||||
seed::input![
|
||||
attrs![At::Type => "range"; At::Step => "1"; At::Min => "1"; At::Max => "10"; At::Value => cols],
|
||||
on_cols_change
|
||||
]
|
||||
])
|
||||
.add_child({
|
||||
let body: Vec<Node<Msg>> = (0..rows)
|
||||
.map(|_row| {
|
||||
let tds: Vec<Node<Msg>> = (0..cols)
|
||||
.map(|_col| td![" "])
|
||||
.collect();
|
||||
tr![tds]
|
||||
})
|
||||
.collect();
|
||||
seed::div![
|
||||
class!["tablePreview"],
|
||||
seed::table![tbody![body]],
|
||||
input![attrs![At::Type => "button"; At::Value => "Insert"], on_submit],
|
||||
]
|
||||
})
|
||||
.build()
|
||||
.into_node()
|
||||
}
|
||||
|
||||
fn styled_rte_button(title: &str, icon: Icon, handler: EventHandler<Msg>) -> Node<Msg> {
|
||||
let button = StyledButton::build()
|
||||
.icon(StyledIcon::build(icon).build())
|
||||
|
Loading…
Reference in New Issue
Block a user