mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
fix (form): enhance form app
This commit is contained in:
parent
2babd4205b
commit
8d80cabba0
7 changed files with 75 additions and 32 deletions
|
|
@ -6,6 +6,11 @@
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
.formbuilder a {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: wavy;
|
||||||
|
text-decoration-color: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
.formbuilder input::placeholder, .formbuilder textarea::placeholder {
|
.formbuilder input::placeholder, .formbuilder textarea::placeholder {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
@ -46,3 +51,11 @@
|
||||||
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
|
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
|
||||||
transition: border-color 0.2s ease-out;
|
transition: border-color 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.formbuilder .banner {
|
||||||
|
background: var(--bg-color);
|
||||||
|
border: 2px solid rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ async function createFormNodes(node, { renderNode, renderLeaf, renderInput, path
|
||||||
else {
|
else {
|
||||||
const currentPath = path.concat(key);
|
const currentPath = path.concat(key);
|
||||||
const $leaf = renderLeaf({
|
const $leaf = renderLeaf({
|
||||||
...withMarkdown(node[key]),
|
...node[key],
|
||||||
path: currentPath,
|
path: currentPath,
|
||||||
label: key,
|
label: key,
|
||||||
});
|
});
|
||||||
|
|
@ -122,15 +122,3 @@ export async function createForm(node, opts) {
|
||||||
});
|
});
|
||||||
return $container;
|
return $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
function withMarkdown(obj) {
|
|
||||||
if (!("description" in obj)) return obj;
|
|
||||||
obj["description"] = toMarkdown(obj["description"]);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toMarkdown(str = "") {
|
|
||||||
str = str.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<a href=\"$2\">$1</a>");
|
|
||||||
str = str.replaceAll("\n", "<br>");
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import rxjs from "../../lib/rx.js";
|
||||||
export function renderLeaf({ format, label, description, type }) {
|
export function renderLeaf({ format, label, description, type }) {
|
||||||
if (label === "banner") return createElement(`
|
if (label === "banner") return createElement(`
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
${description}
|
${fromMarkdown(description)}
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
const $el = createElement(`
|
const $el = createElement(`
|
||||||
|
|
@ -29,6 +29,12 @@ export function renderLeaf({ format, label, description, type }) {
|
||||||
return $el;
|
return $el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fromMarkdown(str = "") {
|
||||||
|
str = str.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<a href=\"$2\">$1</a>");
|
||||||
|
str = str.replaceAll("\n", "<br>");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
export function useForm$($inputNodeList) {
|
export function useForm$($inputNodeList) {
|
||||||
return rxjs.pipe(
|
return rxjs.pipe(
|
||||||
rxjs.mergeMap(() => $inputNodeList()),
|
rxjs.mergeMap(() => $inputNodeList()),
|
||||||
|
|
|
||||||
|
|
@ -98,9 +98,6 @@
|
||||||
}
|
}
|
||||||
.component_page_admin .page_container a {
|
.component_page_admin .page_container a {
|
||||||
color: var(--dark);
|
color: var(--dark);
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-style: wavy;
|
|
||||||
text-decoration-color: rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
}
|
||||||
.component_page_admin .page_container a:hover {
|
.component_page_admin .page_container a:hover {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
|
@ -181,13 +178,6 @@
|
||||||
.component_page_admin .formbuilder textarea::placeholder {
|
.component_page_admin .formbuilder textarea::placeholder {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
.component_page_admin .formbuilder .banner {
|
|
||||||
background: var(--bg-color);
|
|
||||||
border: 2px solid rgba(0, 0, 0, 0.05);
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.component_page_admin .formbuilder .banner pre {
|
.component_page_admin .formbuilder .banner pre {
|
||||||
background: inherit;
|
background: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,6 @@
|
||||||
.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > span span.mandatory {
|
.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > span span.mandatory {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div div.description a {
|
|
||||||
border-bottom: 1px dashed var(--color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.component_formviewer .formbuilder .component_input[disabled],
|
.component_formviewer .formbuilder .component_input[disabled],
|
||||||
.component_formviewer .formbuilder .component_input[readonly],
|
.component_formviewer .formbuilder .component_input[readonly],
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,12 @@ export default function(render, { acl$, getFilename, getDownloadUrl }) {
|
||||||
}
|
}
|
||||||
return $el;
|
return $el;
|
||||||
},
|
},
|
||||||
renderLeaf: ({ label, format, description, required }) => createElement(`
|
renderLeaf: ({ label, format, description, required }) => label === "banner"
|
||||||
|
? createElement(`
|
||||||
|
<div class="banner">
|
||||||
|
${fromMarkdown(safe(description))}
|
||||||
|
</div>
|
||||||
|
`) : createElement(`
|
||||||
<label class="no-select">
|
<label class="no-select">
|
||||||
<div>
|
<div>
|
||||||
<span class="ellipsis">
|
<span class="ellipsis">
|
||||||
|
|
@ -75,7 +80,7 @@ export default function(render, { acl$, getFilename, getDownloadUrl }) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="nothing"></span>
|
<span class="nothing"></span>
|
||||||
<div class="description">${safe(description)}</div>
|
<div class="description">${fromMarkdown(safe(description))}</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
`),
|
`),
|
||||||
|
|
@ -170,3 +175,9 @@ function readOnlyForm(formSpec) {
|
||||||
}
|
}
|
||||||
return formSpec;
|
return formSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fromMarkdown(str = "") {
|
||||||
|
str = str.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<a href=\"$2\">$1</a>");
|
||||||
|
str = str.replaceAll("\n", "<br>");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,21 +53,34 @@ func (this PSQL) Cat(path string) (io.ReadCloser, error) {
|
||||||
forms := make([]FormElement, len(c))
|
forms := make([]FormElement, len(c))
|
||||||
for i, _ := range columns {
|
for i, _ := range columns {
|
||||||
forms[i] = createFormElement(col[i], columns[i])
|
forms[i] = createFormElement(col[i], columns[i])
|
||||||
|
if columnComment := _findCommentColumn(this.ctx, this.db, l.table, columns[i].Name); columnComment != "" {
|
||||||
|
forms[i].Description = columnComment
|
||||||
|
}
|
||||||
if slices.Contains(columns[i].Constraint, "PRIMARY KEY") && forms[i].Value != nil {
|
if slices.Contains(columns[i].Constraint, "PRIMARY KEY") && forms[i].Value != nil {
|
||||||
forms[i].ReadOnly = true
|
forms[i].ReadOnly = true
|
||||||
} else if slices.Contains(columns[i].Constraint, "FOREIGN KEY") {
|
} else if slices.Contains(columns[i].Constraint, "FOREIGN KEY") {
|
||||||
if link, err := _findRelation(this.ctx, this.db, columns[i]); err == nil {
|
if link, err := _findRelation(this.ctx, this.db, columns[i]); err == nil {
|
||||||
forms[i].Description = _createDescription(columns[i], link)
|
|
||||||
if len(link.values) > 0 {
|
if len(link.values) > 0 {
|
||||||
forms[i].Type = "select"
|
forms[i].Type = "select"
|
||||||
forms[i].Opts = link.values
|
forms[i].Opts = link.values
|
||||||
}
|
}
|
||||||
|
if forms[i].Description == "" {
|
||||||
|
forms[i].Description = _createDescription(columns[i], link)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if values, err := _findEnumValues(this.ctx, this.db, columns[i]); err == nil && len(values) > 0 {
|
} else if values, err := _findEnumValues(this.ctx, this.db, columns[i]); err == nil && len(values) > 0 {
|
||||||
forms[i].Type = "select"
|
forms[i].Type = "select"
|
||||||
forms[i].Opts = values
|
forms[i].Opts = values
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if comment := _findCommentTable(this.ctx, this.db, l.table); comment != "" {
|
||||||
|
forms = append([]FormElement{
|
||||||
|
{
|
||||||
|
Name: "banner",
|
||||||
|
Type: "hidden",
|
||||||
|
Description: comment,
|
||||||
|
},
|
||||||
|
}, forms...)
|
||||||
}
|
}
|
||||||
b, err := Form{Elmnts: forms}.MarshalJSON()
|
b, err := Form{Elmnts: forms}.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -78,11 +91,36 @@ func (this PSQL) Cat(path string) (io.ReadCloser, error) {
|
||||||
|
|
||||||
func _createDescription(el Column, link LocationColumn) string {
|
func _createDescription(el Column, link LocationColumn) string {
|
||||||
if slices.Contains(el.Constraint, "FOREIGN KEY") {
|
if slices.Contains(el.Constraint, "FOREIGN KEY") {
|
||||||
return fmt.Sprintf("points to <%s> → <%s>", link.table, link.column)
|
return fmt.Sprintf("points to [<%s> → <%s>](/files/%s/)", link.table, link.column, link.table)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _findCommentTable(ctx context.Context, db *sql.DB, tableName string) string {
|
||||||
|
var comment string
|
||||||
|
if err := db.QueryRowContext(ctx, `
|
||||||
|
SELECT obj_description(c.oid)
|
||||||
|
FROM pg_class c
|
||||||
|
WHERE c.relname = $1 AND c.relkind = 'r'
|
||||||
|
`, tableName).Scan(&comment); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
|
||||||
|
func _findCommentColumn(ctx context.Context, db *sql.DB, tableName, columnName string) string {
|
||||||
|
var comment string
|
||||||
|
if err := db.QueryRowContext(ctx, `
|
||||||
|
SELECT col_description(c.oid, a.attnum)
|
||||||
|
FROM pg_class c
|
||||||
|
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||||
|
WHERE c.relname = $1 AND a.attname = $2 AND c.relkind = 'r'
|
||||||
|
`, tableName, columnName).Scan(&comment); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
|
||||||
func _findRelation(ctx context.Context, db *sql.DB, el Column) (LocationColumn, error) {
|
func _findRelation(ctx context.Context, db *sql.DB, el Column) (LocationColumn, error) {
|
||||||
l := LocationColumn{}
|
l := LocationColumn{}
|
||||||
rows, err := db.QueryContext(ctx, `
|
rows, err := db.QueryContext(ctx, `
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue