Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ panic = "abort"
wasm-bindgen = "0.2.84"
serde = { version="^1.0.160", features = ["derive"] }
serde-wasm-bindgen = "0.5.0"
serde_json = "1.0"
zip = { version = "0.6.4", default-features = false, features = ["deflate"] }
wee_alloc = { version = "0.4.5", optional = true }
console_error_panic_hook = { version = "0.1.6", optional = true }
Expand Down
31 changes: 31 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Routing Example</title>
</head>
<body>
<nav>
<a href="./cdn.html">CDN</a> |
<a href="./lib.html">Lib</a> |
<a href="./worker.html">Worker</a>
</nav>
<div id="app"></div>

<script>
const routes = {
cdn: '<h2>CDN Page</h2><p>This is cdn.html content.</p>',
lib: '<h2>Lib Page</h2><p>This is lib.html content.</p>',
worker: '<h2>Worker Page</h2><p>This is worker.html content.</p>',
};

function renderRoute() {
const hash = location.hash.replace('#', '');
document.getElementById('app').innerHTML = routes[hash] || '<h2>Welcome</h2><p>Select a page.</p>';
}

window.addEventListener('hashchange', renderRoute);
window.addEventListener('DOMContentLoaded', renderRoute);
</script>
</body>
</html>
File renamed without changes.
14 changes: 12 additions & 2 deletions example/datasets.js → public/datasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ var example = {
],
[
{
v: null,
s: 1,
v: "This is a very long text that will demonstrate how the cell handles long content.",
s: 17,
},
{
v: null,
Expand Down Expand Up @@ -846,5 +846,15 @@ var example = {
fontSize: "13.333333px",
format: "General",
},
{
verticalAlign: "center",
color: "rgba(0,0,0,1)",
fontWeight: "bold",
fontFamily: "Calibri",
background: "rgba(247,247,247,1)",
fontSize: "13.333333px",
format: "General",
textWrap: true
}
],
};
File renamed without changes.
File renamed without changes.
11 changes: 8 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use zip::write::FileOptions;
use std::io::Cursor;

use std::collections::HashMap;
use serde_json::Value;

type Dict = HashMap<String, String>;
type Dict = HashMap<String, Value>;

pub mod xml;
use crate::xml::Element;
Expand Down Expand Up @@ -412,13 +413,17 @@ fn get_styles_data(style_table: StyleTable) -> String {
.add_attr("applyBorder", "1")
.add_attr("borderId", id.to_string());
});
if p.align_h.is_some() || p.align_v.is_some() {
if p.align_h.is_some() || p.align_v.is_some() || p.wrap_text {
let mut alignment = Element::new("alignment");

p.align_h.as_ref().map(|v| alignment.add_attr("horizontal", v));
p.align_v.as_ref().map(|v| alignment.add_attr("vertical", v));
if p.wrap_text {
alignment.add_attr("wrapText", "1");
}

xf.add_attr("applyAlignment", "1").add_children(vec![alignment]);
}

xf
}).collect();

Expand Down
127 changes: 81 additions & 46 deletions src/style.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::HashMap;
use serde_json::Value;

use crate::xml::Element;

Expand Down Expand Up @@ -33,6 +34,7 @@ pub struct StyleProps {
pub border: Option<Border>,
pub align_h: Option<String>,
pub align_v: Option<String>,
pub wrap_text: bool,
}

#[derive(PartialEq, Debug)]
Expand Down Expand Up @@ -77,10 +79,11 @@ pub struct XFSProps {
pub format_id: Option<usize>,
pub align_h: Option<String>,
pub align_v: Option<String>,
pub wrap_text: bool,
}

impl StyleTable {
pub fn new(css: Option<Vec<HashMap<String, String>>>) -> StyleTable {
pub fn new(css: Option<Vec<HashMap<String, Value>>>) -> StyleTable {
let mut table = StyleTable {
fonts: vec![Font::new()],
fills: vec![Fill::new(None, "none"), Fill::new(None, "gray125")],
Expand All @@ -99,7 +102,7 @@ impl StyleTable {

table
}
pub fn add(&mut self, style: HashMap<String, String>) {
pub fn add(&mut self, style: HashMap<String, Value>) {
let mut xsf_props: XFSProps = XFSProps::new();
let st = style_to_props(&style);

Expand Down Expand Up @@ -133,21 +136,27 @@ impl StyleTable {
);
xsf_props.align_h = st.align_h;
xsf_props.align_v = st.align_v;
xsf_props.format_id = style.get("format").map(|format_name| {
let i = match get_format_code(format_name) {
Some(format) => format,
None => {
if self.custom_formats.contains_key(format_name) {
self.custom_formats.get(format_name).unwrap().to_owned()
} else {
let index = self.next_custom_format;
self.custom_formats.insert(format_name.to_owned(), index);
self.next_custom_format += 1;
index
}
xsf_props.wrap_text = st.wrap_text;
xsf_props.format_id = style.get("format").and_then(|format_name| {
match format_name {
Value::String(s) => {
let i = match get_format_code(s) {
Some(format) => format,
None => {
if self.custom_formats.contains_key(s) {
self.custom_formats.get(s).unwrap().to_owned()
} else {
let index = self.next_custom_format;
self.custom_formats.insert(s.to_owned(), index);
self.next_custom_format += 1;
index
}
}
};
Some(i as usize)
}
};
i as usize
_ => None,
}
});

self.xfs.push(xsf_props);
Expand Down Expand Up @@ -228,6 +237,7 @@ impl StyleProps {
font: None,
border: None,
align_v: None,
wrap_text: false,
}
}
}
Expand All @@ -241,36 +251,61 @@ impl XFSProps {
format_id: None,
align_h: None,
align_v: None,
wrap_text: false,
}
}
}

fn style_to_props(styles: &HashMap<String, String>) -> StyleProps {
fn style_to_props(styles: &HashMap<String, Value>) -> StyleProps {
let mut font: Font = Font::new();
let mut border: Border = Border::new();
let mut st = StyleProps::new();

for (key, value) in styles {
match key.as_ref() {
"background" => match color_to_argb(value) {
Some(v) => st.fill = Some(Fill::new(Some(v), "solid")),
None => (),
"background" => match value {
Value::String(s) => match color_to_argb(s) {
Some(v) => st.fill = Some(Fill::new(Some(v), "solid")),
None => (),
},
_ => (),
},
"color" => font.color = color_to_argb(value),
"fontWeight" => font.bold = value == "bold",
"fontStyle" => font.italic = value == "italic",
"fontFamily" => font.name = Some(value.to_string()),
"textDecoration" => {
font.underline = value.contains("underline");
font.strike = value.contains("line-through");
"color" => match value {
Value::String(s) => font.color = color_to_argb(s),
_ => (),
},
"fontWeight" => font.bold = value.as_str().map(|s| s == "bold").unwrap_or(false),
"fontStyle" => font.italic = value.as_str().map(|s| s == "italic").unwrap_or(false),
"fontFamily" => font.name = value.as_str().map(|s| s.to_string()),
"textDecoration" => match value {
Value::String(s) => {
font.underline = s.contains("underline");
font.strike = s.contains("line-through");
}
_ => (),
},
"fontSize" => font.size = value.as_str().and_then(|s| px_to_pt(s)),
"align" => st.align_h = value.as_str().map(|s| s.to_string()),
"verticalAlign" => st.align_v = value.as_str().map(|s| s.to_string()),
"borderTop" => border.top = value.as_str().and_then(|s| str_to_border(s, BorderPosition::Top)),
"borderRight" => border.right = value.as_str().and_then(|s| str_to_border(s, BorderPosition::Right)),
"borderBottom" => border.bottom = value.as_str().and_then(|s| str_to_border(s, BorderPosition::Bottom)),
"borderLeft" => border.left = value.as_str().and_then(|s| str_to_border(s, BorderPosition::Left)),
"textWrap" => {
// accept boolean, string or number values
match value {
Value::Bool(b) => st.wrap_text = *b,
Value::String(s) => st.wrap_text = s == "true" || s == "1",
Value::Number(n) => {
if let Some(i) = n.as_i64() {
st.wrap_text = i != 0;
} else if let Some(f) = n.as_f64() {
st.wrap_text = f != 0.0;
}
}
_ => (),
}
}
"fontSize" => font.size = px_to_pt(&value),
"align" => st.align_h = Some(value.to_owned()),
"verticalAlign" => st.align_v = Some(value.to_owned()),
"borderTop" => border.top = str_to_border(&value, BorderPosition::Top),
"borderRight" => border.right = str_to_border(&value, BorderPosition::Right),
"borderBottom" => border.bottom = str_to_border(&value, BorderPosition::Bottom),
"borderLeft" => border.left = str_to_border(&value, BorderPosition::Left),
_ => (),
}
}
Expand Down Expand Up @@ -463,20 +498,20 @@ fn str_to_border(v: &str, pos: BorderPosition) -> Option<BorderProps> {

#[test]
fn style_to_props_test() {
let mut styles: HashMap<String, String> = HashMap::new();
styles.insert(String::from("background"), String::from("#FF0000"));
styles.insert(String::from("color"), String::from("#FFFF00"));
styles.insert(String::from("fontWeight"), String::from("bold"));
styles.insert(String::from("fontStyle"), String::from("italic"));
styles.insert(String::from("fontSize"), String::from("24px"));
styles.insert(String::from("fontFamily"), String::from("Calibri"));
styles.insert(String::from("textDecoration"), String::from("underline"));
styles.insert(String::from("align"), String::from("left"));
styles.insert(String::from("verticalAlign"), String::from("bottom"));
styles.insert(String::from("borderTop"), String::from("1px solid #9AFF02"));
let mut styles: HashMap<String, Value> = HashMap::new();
styles.insert(String::from("background"), Value::String(String::from("#FF0000")));
styles.insert(String::from("color"), Value::String(String::from("#FFFF00")));
styles.insert(String::from("fontWeight"), Value::String(String::from("bold")));
styles.insert(String::from("fontStyle"), Value::String(String::from("italic")));
styles.insert(String::from("fontSize"), Value::String(String::from("24px")));
styles.insert(String::from("fontFamily"), Value::String(String::from("Calibri")));
styles.insert(String::from("textDecoration"), Value::String(String::from("underline")));
styles.insert(String::from("align"), Value::String(String::from("left")));
styles.insert(String::from("verticalAlign"), Value::String(String::from("bottom")));
styles.insert(String::from("borderTop"), Value::String(String::from("1px solid #9AFF02")));
styles.insert(
String::from("borderRight"),
String::from("1px solid #000000"),
Value::String(String::from("1px solid #000000")),
);

let st = style_to_props(&styles);
Expand Down