language selector, but not quite workinhg yet
This commit is contained in:
parent
36bcf7277c
commit
e7701f4b7d
|
@ -4,7 +4,7 @@ Easily create EPUB e-book files with proper RTL support.
|
||||||
|
|
||||||
This is a web app that uses [pandoc](https://pandoc.org) to create .epub files for e-books in RTL languages. Making RTL e-books can be tricky. This tries app tries to simplify the process as much as possible, so that anyone can make them.
|
This is a web app that uses [pandoc](https://pandoc.org) to create .epub files for e-books in RTL languages. Making RTL e-books can be tricky. This tries app tries to simplify the process as much as possible, so that anyone can make them.
|
||||||
|
|
||||||
[Try it live - RTL EPUB Maker](https://rtl-epub-maker.lingdocs.com)
|
### [Try it live - RTL EPUB Maker 📚](https://rtl-epub-maker.lingdocs.com)
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ If you are using `linux/amd64` architecture you can just run the [the docker ima
|
||||||
docker compose up
|
docker compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using an architecture other than `linux/amd64` you will need to build your own Docker image.
|
If you are using an architecture other than `linux/amd64` you will need to build your own docker image.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker build . -t rtl-epub-maker
|
docker build . -t rtl-epub-maker
|
||||||
|
@ -52,4 +52,4 @@ The app will be served on `http://localhost:3001`. Add a reverse proxy with SSL
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Code is licensed under a [MIT License](https://github.com/lingdocs/rtl-epub-maker/blob/master/LICENSE). Contributions are welcome.
|
Code is licensed under an [MIT License](https://github.com/lingdocs/rtl-epub-maker/blob/master/LICENSE). Contributions are welcome.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ChangeEvent, useState, useRef } from "react";
|
import { ChangeEvent, useState, useRef } from "react";
|
||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
|
import LanguageSelect from "./LanguageSelect";
|
||||||
|
|
||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
"title",
|
"title",
|
||||||
|
@ -20,8 +21,7 @@ type Option = {
|
||||||
label: string,
|
label: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseSettings = {
|
const baseSettings = {
|
||||||
language: "ps-AF",
|
|
||||||
dir: "rtl",
|
dir: "rtl",
|
||||||
"page-progression-direction": "rtl",
|
"page-progression-direction": "rtl",
|
||||||
};
|
};
|
||||||
|
@ -55,6 +55,21 @@ function BookInfoInput({ handleSubmit }: { handleSubmit: (info: { frontmatter: F
|
||||||
[name]: value,
|
[name]: value,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
function handleLanguageChange(lang: string | null) {
|
||||||
|
setState(s => {
|
||||||
|
if (!lang) {
|
||||||
|
delete s.lang;
|
||||||
|
delete s.language;
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...s,
|
||||||
|
// TODO: using both, but which is proper/necessary?
|
||||||
|
lang,
|
||||||
|
language: lang,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
function submit() {
|
function submit() {
|
||||||
const cover = coverRef.current.files[0] as (File | undefined);
|
const cover = coverRef.current.files[0] as (File | undefined);
|
||||||
handleSubmit({
|
handleSubmit({
|
||||||
|
@ -65,24 +80,24 @@ function BookInfoInput({ handleSubmit }: { handleSubmit: (info: { frontmatter: F
|
||||||
cover,
|
cover,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log(state);
|
||||||
|
console.log("will render");
|
||||||
return <div style={{ maxWidth: "500px" }}>
|
return <div style={{ maxWidth: "500px" }}>
|
||||||
<div className="my-3">
|
<div className="my-3">
|
||||||
<label htmlFor="cover-file" className="form-label">cover image <span className="text-muted">(.jpg or .png less than 5mb)</span></label>
|
<label htmlFor="cover-file" className="form-label">cover image <span className="text-muted">(.jpg or .png less than 5mb)</span></label>
|
||||||
<input multiple={false} ref={coverRef} className="form-control" type="file" id="cover-file" accept="image/jpeg,image/png"/>
|
<input multiple={false} ref={coverRef} className="form-control" type="file" id="cover-file" accept="image/jpeg,image/png"/>
|
||||||
</div>
|
</div>
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
<div key={field} className="d-flex flex-row align-items-end mb-2">
|
<div className="mb-2">
|
||||||
<div className="col-auto" style={{ width: "100%" }}>
|
<label htmlFor={field} className="form-label d-flex flex-row align-items-center">
|
||||||
<label htmlFor={field} className="form-label d-flex flex-row align-items-center">
|
{!requiredFields.includes(field) && <span className="me-2">
|
||||||
{!requiredFields.includes(field) && <span className="me-2">
|
<button type="button" className="btn btn-sm btn-outline-secondary" onClick={() => handleRemoveField(field)}>
|
||||||
<button type="button" className="btn btn-sm btn-outline-secondary" onClick={() => handleRemoveField(field)}>
|
X
|
||||||
X
|
</button>
|
||||||
</button>
|
</span>}
|
||||||
</span>}
|
<span>{field}</span>
|
||||||
<span>{field}</span>
|
</label>
|
||||||
</label>
|
<input onChange={handleFieldChange} type="text" className="form-control" id={field} name={field} value={state[field]} />
|
||||||
<input onChange={handleFieldChange} type="text" className="form-control" id={field} name={field} value={state[field]} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className="mt-4 mb-2">add fields:</div>
|
<div className="mt-4 mb-2">add fields:</div>
|
||||||
|
@ -97,6 +112,7 @@ function BookInfoInput({ handleSubmit }: { handleSubmit: (info: { frontmatter: F
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options={availableFieldsOptions}
|
options={availableFieldsOptions}
|
||||||
/>
|
/>
|
||||||
|
<LanguageSelect value={state.lang} onChange={handleLanguageChange} />
|
||||||
<button onClick={submit} type="button" className="btn btn-primary my-4">Create .epub</button>
|
<button onClick={submit} type="button" className="btn btn-primary my-4">Create .epub</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import Select from "react-select";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const languageOptions = [
|
||||||
|
{ value: "ar", label: "Arabic" },
|
||||||
|
{ value: "fa", label: "Farsi" },
|
||||||
|
{ value: "prs", label: "Dari" },
|
||||||
|
{ value: "ps", label: "Pashto" },
|
||||||
|
{ value: "ps-AF", label: "Pashto (Afghanistan) "},
|
||||||
|
{ value: "ps-PK", label: "Pashto (Pakistan) "},
|
||||||
|
{ value: "ur", label: "Urdu" },
|
||||||
|
{ value: "other", label: "Other..." },
|
||||||
|
];
|
||||||
|
|
||||||
|
function LanguageSelect({ value, onChange }: {
|
||||||
|
value: string | null,
|
||||||
|
onChange: (language: string | null) => void,
|
||||||
|
}) {
|
||||||
|
const [showingOther, setShowingOther] = useState<boolean>(false);
|
||||||
|
function handleChange(o: { value: string, label: string }) {
|
||||||
|
if (!o) {
|
||||||
|
onChange(null);
|
||||||
|
} else if (o.value === "other") {
|
||||||
|
setShowingOther(true);
|
||||||
|
onChange(null);
|
||||||
|
} else {
|
||||||
|
if (showingOther) setShowingOther(false);
|
||||||
|
onChange(o.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <div>
|
||||||
|
<div className="mt-4 mb-2">language:</div>
|
||||||
|
<Select
|
||||||
|
className="basic-single"
|
||||||
|
classNamePrefix="select"
|
||||||
|
isClearable={true}
|
||||||
|
value={typeof value === "number" ? null : languageOptions.find(o => value === o.value)}
|
||||||
|
isSearchable
|
||||||
|
// @ts-ignore
|
||||||
|
onChange={handleChange}
|
||||||
|
// @ts-ignore
|
||||||
|
options={languageOptions}
|
||||||
|
/>
|
||||||
|
{showingOther && <div className="my-2">
|
||||||
|
<label htmlFor="otherLang" className="form-label d-flex flex-row align-items-center">
|
||||||
|
<span>Custom <a href="https://www.w3.org/International/articles/language-tags/" target="_blank">IETF BCP 47</a> Language Code</span>
|
||||||
|
</label>
|
||||||
|
{/* TODO: for some reason can't use value={value} with this - but it still works */}
|
||||||
|
<input onChange={(e) => onChange(e.target.value)} type="text" className="form-control" id="otherLang" />
|
||||||
|
</div>}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LanguageSelect;
|
Loading…
Reference in New Issue