From fb9ec75c46bc237bc6c8df24ee998e6de90168a1 Mon Sep 17 00:00:00 2001 From: mehekj Date: Wed, 12 Apr 2023 20:55:14 -0400 Subject: readonly fields and schema key info display --- src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 13e45963e..42c9f9d48 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -9,9 +9,11 @@ import { KeyValueBox } from '../../nodes/KeyValueBox'; import { DefaultStyleProvider } from '../../StyleProvider'; import { CollectionSchemaView } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; +import { computed } from 'mobx'; export interface SchemaTableCellProps { Document: Doc; + schemaView: CollectionSchemaView | undefined; fieldKey: string; columnWidth: number; isRowActive: () => boolean | undefined; @@ -20,6 +22,10 @@ export interface SchemaTableCellProps { @observer export class SchemaTableCell extends React.Component { + get readOnly() { + return this.props.schemaView?.keyInfos[this.props.fieldKey].readOnly; + } + render() { const props: FieldViewProps = { Document: this.props.Document, @@ -50,7 +56,7 @@ export class SchemaTableCell extends React.Component { return (
-
+
} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} -- cgit v1.2.3-70-g09d2 From 79791c294e948bc5e9f5799b12dec138c7d8b371 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 20 Apr 2023 01:13:07 -0400 Subject: added schema cell types for images and dates --- package-lock.json | 201 ++++++++++++--------- package.json | 4 +- src/client/documents/Documents.ts | 4 +- .../collectionSchema/CollectionSchemaView.tsx | 44 +++-- .../collectionSchema/SchemaTableCell.tsx | 158 ++++++++++++++-- 5 files changed, 289 insertions(+), 122 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/package-lock.json b/package-lock.json index 5f81e1f0f..7d2ef9509 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5580,6 +5580,16 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "d3": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz", @@ -6960,6 +6970,28 @@ "is-symbol": "^1.0.2" } }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -6971,6 +7003,7 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { + "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -14015,7 +14048,7 @@ "dependencies": { "@iarna/cli": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/@iarna/cli/-/cli-2.1.0.tgz", "integrity": "sha512-rvVVqDa2g860niRbqs3D5RhL4la3dc1vwk+NlpKPZxKaMSHtE2se6C2x8NeveN+rcjp3/686X+u+09CZ+7lmAQ==", "requires": { "glob": "^7.1.2", @@ -14118,7 +14151,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14133,7 +14166,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14142,12 +14175,12 @@ }, "asap": { "version": "2.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "asn1": { "version": "0.2.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "requires": { "safer-buffer": "~2.1.0" @@ -14155,7 +14188,7 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" }, "asynckit": { @@ -14165,22 +14198,22 @@ }, "aws-sign2": { "version": "0.7.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" }, "aws4": { "version": "1.11.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "balanced-match": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "requires": { "tweetnacl": "^0.14.3" @@ -14201,7 +14234,7 @@ }, "bluebird": { "version": "3.7.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "boxen": { @@ -14249,7 +14282,7 @@ }, "cacache": { "version": "12.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "requires": { "bluebird": "^3.5.5", @@ -14286,7 +14319,7 @@ }, "caseless": { "version": "0.12.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, "chalk": { @@ -14430,7 +14463,7 @@ }, "combined-stream": { "version": "1.0.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" @@ -14438,7 +14471,7 @@ }, "concat-map": { "version": "0.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { @@ -14468,7 +14501,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14483,7 +14516,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14492,7 +14525,7 @@ }, "config-chain": { "version": "1.1.13", - "resolved": false, + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "requires": { "ini": "^1.3.4", @@ -14593,7 +14626,7 @@ }, "dashdash": { "version": "1.14.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "requires": { "assert-plus": "^1.0.0" @@ -14626,7 +14659,7 @@ }, "decode-uri-component": { "version": "0.2.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "deep-extend": { @@ -14672,7 +14705,7 @@ }, "dezalgo": { "version": "1.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "requires": { "asap": "^2.0.0", @@ -14724,7 +14757,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14739,7 +14772,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14748,7 +14781,7 @@ }, "ecc-jsbn": { "version": "0.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "requires": { "jsbn": "~0.1.0", @@ -14783,7 +14816,7 @@ }, "env-paths": { "version": "2.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" }, "err-code": { @@ -14867,7 +14900,7 @@ }, "extsprintf": { "version": "1.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" }, "fast-json-stable-stringify": { @@ -14877,12 +14910,12 @@ }, "figgy-pudding": { "version": "3.5.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" }, "filter-obj": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" }, "find-npm-prefix": { @@ -14915,7 +14948,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14930,7 +14963,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14939,12 +14972,12 @@ }, "forever-agent": { "version": "0.6.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" }, "form-data": { "version": "2.3.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { "asynckit": "^0.4.0", @@ -14977,7 +15010,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -14992,7 +15025,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -15060,7 +15093,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -15075,7 +15108,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -15084,7 +15117,7 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "function-bind": { @@ -15174,7 +15207,7 @@ }, "getpass": { "version": "0.1.7", - "resolved": false, + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "requires": { "assert-plus": "^1.0.0" @@ -15182,7 +15215,7 @@ }, "glob": { "version": "7.2.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", @@ -15195,7 +15228,7 @@ "dependencies": { "minimatch": { "version": "3.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" @@ -15238,12 +15271,12 @@ }, "graceful-fs": { "version": "4.2.10", - "resolved": false, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "har-schema": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" }, "har-validator": { @@ -15322,7 +15355,7 @@ }, "http-signature": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "requires": { "assert-plus": "^1.0.0", @@ -15449,7 +15482,7 @@ }, "is-cidr": { "version": "3.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.1.1.tgz", "integrity": "sha512-Gx+oErgq1j2jAKCR2Kbq0b3wbH0vQKqZ0wOlHxm0o56nq51Cs/DZA8oz9dMDhbHyHEGgJ86eTeVudtgMMOx3Mw==", "requires": { "cidr-regex": "^2.0.10" @@ -15528,7 +15561,7 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "isarray": { @@ -15543,12 +15576,12 @@ }, "isstream": { "version": "0.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "jsbn": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "json-parse-better-errors": { @@ -15558,7 +15591,7 @@ }, "json-parse-even-better-errors": { "version": "2.3.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { @@ -15568,7 +15601,7 @@ }, "json-stringify-safe": { "version": "5.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "jsonparse": { @@ -15786,7 +15819,7 @@ }, "lock-verify": { "version": "2.2.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.2.2.tgz", "integrity": "sha512-2CUNtr1ZSVKJHcYP8uEzafmmuyauCB5zZimj8TvQd/Lflt9kXVZs+8S+EbAzZLaVUDn8CYGmeC3DFGdYfnCzeQ==", "requires": { "@iarna/cli": "^2.1.0", @@ -15915,7 +15948,7 @@ }, "meant": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.3.tgz", "integrity": "sha512-88ZRGcNxAq4EH38cQ4D85PM57pikCwS8Z99EWHODxN7KBY+UuPiqzRTtZzS8KTXO/ywSWbdjjJST2Hly/EQxLw==" }, "mime-db": { @@ -15933,7 +15966,7 @@ }, "minimatch": { "version": "3.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" @@ -15982,7 +16015,7 @@ }, "mkdirp": { "version": "0.5.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "requires": { "minimist": "^1.2.6" @@ -16030,7 +16063,7 @@ }, "node-gyp": { "version": "5.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz", "integrity": "sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw==", "requires": { "env-paths": "^2.2.0", @@ -16368,7 +16401,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16383,7 +16416,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16397,7 +16430,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { @@ -16417,7 +16450,7 @@ }, "performance-now": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "pify": { @@ -16466,7 +16499,7 @@ }, "proto-list": { "version": "1.2.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" }, "protoduck": { @@ -16489,7 +16522,7 @@ }, "psl": { "version": "1.9.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "pump": { @@ -16529,12 +16562,12 @@ }, "qs": { "version": "6.5.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" }, "query-string": { "version": "6.14.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "requires": { "decode-uri-component": "^0.2.0", @@ -16545,7 +16578,7 @@ }, "qw": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.2.tgz", "integrity": "sha512-1PhZ/iLKwlVNq45dnerTMKFjMof49uqli7/0QsvPNbX5OJ3IZ8msa9lUpvPheVdP+IYYPrf6cOaVil7S35joVA==" }, "rc": { @@ -16591,7 +16624,7 @@ }, "read-package-json": { "version": "2.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", "requires": { "glob": "^7.1.1", @@ -16650,7 +16683,7 @@ }, "request": { "version": "2.88.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { "aws-sign2": "~0.7.0", @@ -16720,7 +16753,7 @@ }, "safe-buffer": { "version": "5.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { @@ -16891,7 +16924,7 @@ }, "sshpk": { "version": "1.17.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "requires": { "asn1": "~0.2.3", @@ -16947,7 +16980,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16962,7 +16995,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -16976,7 +17009,7 @@ }, "strict-uri-encode": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" }, "string-width": { @@ -17132,7 +17165,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -17147,7 +17180,7 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } @@ -17166,7 +17199,7 @@ }, "tough-cookie": { "version": "2.5.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { "psl": "^1.1.28", @@ -17175,7 +17208,7 @@ "dependencies": { "punycode": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } @@ -17190,7 +17223,7 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "typedarray": { @@ -17261,7 +17294,7 @@ }, "uri-js": { "version": "4.4.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "requires": { "punycode": "^2.1.0" @@ -17302,7 +17335,7 @@ }, "uuid": { "version": "3.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "validate-npm-package-license": { @@ -17324,7 +17357,7 @@ }, "verror": { "version": "1.10.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "requires": { "assert-plus": "^1.0.0", @@ -17832,9 +17865,9 @@ } }, "openai": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-3.1.0.tgz", - "integrity": "sha512-v5kKFH5o+8ld+t0arudj833Mgm3GcgBnbyN9946bj6u7bvel4Yg6YFz2A4HLIYDzmMjIo0s6vSG9x73kOwvdCg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", "requires": { "axios": "^0.26.0", "form-data": "^4.0.0" @@ -22643,6 +22676,12 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index c1ff9fe92..70dcc1845 100644 --- a/package.json +++ b/package.json @@ -157,6 +157,7 @@ "@types/three": "^0.126.2", "@types/web": "0.0.53", "@webscopeio/react-textarea-autocomplete": "^4.9.1", + "D": "^1.0.0", "adm-zip": "^0.4.16", "archiver": "^3.1.1", "array-batcher": "^1.2.3", @@ -189,7 +190,6 @@ "csv-parser": "^3.0.0", "csv-stringify": "^6.3.0", "d3": "^7.6.1", - "D": "^1.0.0", "depcheck": "^0.9.2", "equation-editor-react": "github:bobzel/equation-editor-react#useLocally", "exif": "^0.6.0", @@ -246,7 +246,7 @@ "nodemon": "^1.19.4", "normalize.css": "^8.0.1", "npm": "^6.14.18", - "openai": "^3.1.0", + "openai": "^3.2.1", "p-limit": "^2.2.0", "passport": "^0.4.0", "passport-google-oauth20": "^2.0.0", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bd878ca8a..c5b6546d7 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -68,10 +68,10 @@ class EmptyBox { return ''; } } -export abstract class FInfo { +export class FInfo { description: string = ''; readOnly: boolean = false; - fieldType?: string; + fieldType?: string = ''; values?: Field[]; // format?: string; // format to display values (e.g, decimal places, $, etc) // parse?: ScriptField; // parse a value from a string diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 39e223c66..1f76c8099 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -31,8 +31,18 @@ export enum ColumnType { String, Boolean, Date, + Image, + Any, } +export const FInfotoColType: { [key: string]: ColumnType } = { + string: ColumnType.String, + number: ColumnType.Number, + boolean: ColumnType.Boolean, + date: ColumnType.Date, + image: ColumnType.Image, +}; + const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate', 'text']; @observer @@ -42,7 +52,7 @@ export class CollectionSchemaView extends CollectionSubView() { private _makeNewColumn: boolean = false; private _documentOptions: DocumentOptions = new DocumentOptions(); - public static _rowHeight: number = 40; + public static _rowHeight: number = 50; public static _minColWidth: number = 25; public static _rowMenuWidth: number = 60; public static _previewDividerWidth: number = 4; @@ -55,7 +65,7 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _colEles: HTMLDivElement[] = []; @observable _displayColumnWidths: number[] | undefined; @observable _columnMenuIndex: number | undefined; - @observable _menuOptions: [string, { description: string; type: string; readOnly: boolean }][] = []; + @observable _fieldInfos: [string, FInfo][] = []; @observable _newFieldWarning: string = ''; @observable _makeNewField: boolean = false; @observable _newFieldDefault: any = 0; @@ -64,7 +74,7 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _filterColumnIndex: number | undefined; @observable _filterSearchValue: string = ''; - get keyInfos() { + get fieldInfos() { const docs = this.childDocs; const keys: { [key: string]: boolean } = {}; // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. @@ -75,23 +85,23 @@ export class CollectionSchemaView extends CollectionSubView() { //TODO Types untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))))); - // this.columns.forEach(key => (keys[key.heading] = true)); + let computedKeys: { [key: string]: FInfo } = {}; - let computedKeys: { [key: string]: { description: string; type: string; readOnly: boolean } } = {}; - Object.keys(keys).forEach((key: string) => { - computedKeys[key] = { description: '', type: '', readOnly: false }; + Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => { + computedKeys[pair[0]] = pair[1]; }); - Object.entries(this._documentOptions).forEach((pair: [string, any]) => { - const info: FInfo = pair[1]; - computedKeys[pair[0]] = { description: info.description, type: info.fieldType ?? '', readOnly: info.readOnly }; + Object.keys(keys).forEach((key: string) => { + if (!(key in computedKeys)) { + computedKeys[key] = new FInfo(''); + } }); return computedKeys; } get documentKeys() { - return Object.keys(this.keyInfos); + return Object.keys(this.fieldInfos); } @computed get previewWidth() { @@ -497,7 +507,7 @@ export class CollectionSchemaView extends CollectionSubView() { onSearchKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Enter': - const menuKeys = Object.keys(this._menuOptions); + const menuKeys = Object.keys(this._fieldInfos); menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(menuKeys[0]) : action(() => (this._makeNewField = true))(); break; case 'Escape': @@ -527,7 +537,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._makeNewColumn = false; this._columnMenuIndex = index; this._menuValue = ''; - this._menuOptions = Object.entries(this.keyInfos); + this._fieldInfos = Object.entries(this.fieldInfos); this._makeNewField = false; this._newFieldWarning = ''; this._makeNewField = false; @@ -573,7 +583,7 @@ export class CollectionSchemaView extends CollectionSubView() { @action updateKeySearch = (e: React.ChangeEvent) => { this._menuValue = e.target.value; - this._menuOptions = Object.entries(this.keyInfos).filter(value => value[0].toLowerCase().includes(this._menuValue.toLowerCase())); + this._fieldInfos = Object.entries(this.fieldInfos).filter(value => value[0].toLowerCase().includes(this._menuValue.toLowerCase())); }; getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); @@ -675,7 +685,7 @@ export class CollectionSchemaView extends CollectionSubView() { { passive: false } ) }> - {this._menuOptions.map(([key, info]) => ( + {this._fieldInfos.map(([key, info]) => (
{ @@ -685,10 +695,10 @@ export class CollectionSchemaView extends CollectionSubView() {

{key} - {info.type ? ', ' : ''} + {info.fieldType ? ', ' : ''} - {info.type} + {info.fieldType}

{info.description}

diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index f9319050e..4e31b1e1e 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,15 +1,20 @@ import React = require('react'); import { observer } from 'mobx-react'; -import { Doc, Field } from '../../../../fields/Doc'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; +import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; +import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; import { Transform } from '../../../util/Transform'; import { EditableView } from '../../EditableView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { DefaultStyleProvider } from '../../StyleProvider'; -import { CollectionSchemaView } from './CollectionSchemaView'; +import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; -import { computed } from 'mobx'; +import { action, computed, observable } from 'mobx'; +import { extname } from 'path'; +import { Cast, DateCast } from '../../../../fields/Types'; +import { ImageField } from '../../../../fields/URLField'; +import { DateField } from '../../../../fields/DateField'; +import DatePicker from 'react-datepicker'; export interface SchemaTableCellProps { Document: Doc; @@ -23,10 +28,10 @@ export interface SchemaTableCellProps { @observer export class SchemaTableCell extends React.Component { get readOnly() { - return this.props.schemaView?.keyInfos[this.props.fieldKey].readOnly; + return this.props.schemaView?.fieldInfos[this.props.fieldKey]?.readOnly ?? false; } - render() { + get defaultCellContent() { const props: FieldViewProps = { Document: this.props.Document, docFilters: returnEmptyFilter, @@ -53,21 +58,134 @@ export class SchemaTableCell extends React.Component { }; return ( -
-
- } - GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} - SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { - if (shiftDown && enterKey) { - this.props.setColumnValues(this.props.fieldKey, value); - } - return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); - }} - editing={this.props.isRowActive() ? undefined : false} - /> -
+
+ } + GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} + SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { + if (shiftDown && enterKey) { + this.props.setColumnValues(this.props.fieldKey, value); + } + return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); + }} + editing={this.props.isRowActive() ? undefined : false} + /> +
+ ); + } + + getCellWithContent(content: any) { + return ( +
+ {content}
); } + + get getCellType() { + const columnTypeStr = this.props.schemaView?.fieldInfos[this.props.fieldKey]?.fieldType; + if (columnTypeStr) { + if (columnTypeStr in FInfotoColType) { + return FInfotoColType[columnTypeStr]; + } + + return ColumnType.Any; + } + + const cellValue = this.props.Document[this.props.fieldKey]; + if (cellValue instanceof ImageField) return ColumnType.Image; + if (cellValue instanceof DateField) return ColumnType.Date; + + return ColumnType.Any; + } + + render() { + const cellType: ColumnType = this.getCellType; + switch (cellType) { + case ColumnType.Image: + return ; + case ColumnType.Date: + return ; + default: + return this.getCellWithContent(this.defaultCellContent); + } + } +} + +// mj: most of this is adapted from old schema code so I'm not sure what it does tbh +@observer +export class SchemaImageCell extends SchemaTableCell { + choosePath(url: URL) { + if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href + if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver + if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question + + const ext = extname(url.href); + return url.href.replace(ext, '_o' + ext); + } + + get content() { + const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc + const alts = DocListCast(this.props.Document[this.props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images + const altpaths = alts + .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url) + .filter(url => url) + .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents + const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; + // If there is a path, follow it; otherwise, follow a link to a default image icon + const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; + + const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio + let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px + const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px + width = height * aspect; // increase the width of the image if necessary to maintain proportionality + + return ; + } + + render() { + return this.getCellWithContent(this.content); + } +} + +@observer +export class SchemaDateCell extends SchemaTableCell { + @observable _pickingDate: boolean = false; + + @computed get date(): DateField { + // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined. + return DateCast(this.props.Document[this.props.fieldKey]); + } + + @action + handleChange = (date: any) => { + // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } }); + // if (script.compiled) { + // this.applyToDoc(this._document, this.props.row, this.props.col, script.run); + // } else { + // ^ DateCast is always undefined for some reason, but that is what the field should be set to + this.props.Document[this.props.fieldKey] = new DateField(date as Date); + //} + }; + + get content() { + return !this._pickingDate ? ( +
(this._pickingDate = true))}>{this.defaultCellContent}
+ ) : ( + { + this.handleChange(date); + this._pickingDate = false; + }} + onChange={(date: any) => this.handleChange(date)} + /> + ); + } + + render() { + return this.getCellWithContent(this.content); + } } -- cgit v1.2.3-70-g09d2 From 99326044a2d81a9f0d0312b4d209946e0f119b78 Mon Sep 17 00:00:00 2001 From: mehekj Date: Mon, 24 Apr 2023 17:47:54 -0400 Subject: crappy version of schema image cell preview on hover --- .../collections/collectionSchema/SchemaRowBox.tsx | 2 +- .../collectionSchema/SchemaTableCell.tsx | 65 ++++++++++++++-------- 2 files changed, 43 insertions(+), 24 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 59b571b58..79808d8f8 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -88,7 +88,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { return (
{ diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 4e31b1e1e..2325339fc 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -58,7 +58,7 @@ export class SchemaTableCell extends React.Component { }; return ( -
+
} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} @@ -76,9 +76,7 @@ export class SchemaTableCell extends React.Component { getCellWithContent(content: any) { return ( -
+
{content}
); @@ -107,7 +105,7 @@ export class SchemaTableCell extends React.Component { case ColumnType.Image: return ; case ColumnType.Date: - return ; + // return ; default: return this.getCellWithContent(this.defaultCellContent); } @@ -117,16 +115,18 @@ export class SchemaTableCell extends React.Component { // mj: most of this is adapted from old schema code so I'm not sure what it does tbh @observer export class SchemaImageCell extends SchemaTableCell { + @observable _previewRef: HTMLImageElement | undefined; + choosePath(url: URL) { if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question const ext = extname(url.href); - return url.href.replace(ext, '_o' + ext); + return url.href.replace(ext, '_s' + ext); } - get content() { + get url() { const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc const alts = DocListCast(this.props.Document[this.props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images const altpaths = alts @@ -136,13 +136,42 @@ export class SchemaImageCell extends SchemaTableCell { const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; // If there is a path, follow it; otherwise, follow a link to a default image icon const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; + return url[0]; + } + + @action + showHoverPreview = (e: React.PointerEvent) => { + this._previewRef = document.createElement('img'); + document.body.appendChild(this._previewRef); + const ext = extname(this.url); + this._previewRef.src = this.url.replace('_s' + ext, '_m' + ext); + this._previewRef.style.position = 'absolute'; + this._previewRef.style.left = e.clientX + 10 + 'px'; + this._previewRef.style.top = e.clientY + 10 + 'px'; + this._previewRef.style.zIndex = '1000'; + }; + + @action + moveHoverPreview = (e: React.PointerEvent) => { + if (!this._previewRef) return; + this._previewRef.style.left = e.clientX + 10 + 'px'; + this._previewRef.style.top = e.clientY + 10 + 'px'; + }; + @action + removeHoverPreview = (e: React.PointerEvent) => { + if (!this._previewRef) return; + document.body.removeChild(this._previewRef); + }; + + get content() { const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio - let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px - const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px - width = height * aspect; // increase the width of the image if necessary to maintain proportionality + // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px + // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px + const height = CollectionSchemaView._rowHeight - 10; + const width = height * aspect; // increase the width of the image if necessary to maintain proportionality - return ; + return ; } render() { @@ -171,18 +200,8 @@ export class SchemaDateCell extends SchemaTableCell { }; get content() { - return !this._pickingDate ? ( -
(this._pickingDate = true))}>{this.defaultCellContent}
- ) : ( - { - this.handleChange(date); - this._pickingDate = false; - }} - onChange={(date: any) => this.handleChange(date)} - /> - ); + return this.handleChange(date)} />; + // return !this._pickingDate ?
(this._pickingDate = true))}>{this.defaultCellContent}
: this.handleChange(date)} />; } render() { -- cgit v1.2.3-70-g09d2 From bc550e43b6042176cf59f04cbff51e1b21adaa8a Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 27 Apr 2023 00:41:43 -0400 Subject: trying to get schema keyboard controls working --- .../collectionSchema/CollectionSchemaView.tsx | 43 ++++++++++++-- .../collections/collectionSchema/SchemaRowBox.tsx | 1 + .../collectionSchema/SchemaTableCell.tsx | 65 +++++++++++++--------- 3 files changed, 78 insertions(+), 31 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index f6dc2a3d7..92a04f5ec 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -74,6 +74,7 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _menuValue: string = ''; @observable _filterColumnIndex: number | undefined; @observable _filterSearchValue: string = ''; + @observable _selectedCell: [Doc, number] | undefined; get fieldInfos() { const docs = this.childDocs; @@ -174,8 +175,11 @@ export class CollectionSchemaView extends CollectionSubView() { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[lastIndex + 1]; if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); - else this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1); - this.scrollToDoc(newDoc, {}); + else { + this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1); + this._selectedCell && (this._selectedCell[0] = newDoc); + this.scrollToDoc(newDoc, {}); + } } e.stopPropagation(); e.preventDefault(); @@ -191,13 +195,37 @@ export class CollectionSchemaView extends CollectionSubView() { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[firstIndex - 1]; if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); - else this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1); - this.scrollToDoc(newDoc, {}); + else { + this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1); + this._selectedCell && (this._selectedCell[0] = newDoc); + this.scrollToDoc(newDoc, {}); + } } e.stopPropagation(); e.preventDefault(); } break; + case 'ArrowRight': + if (this._selectedCell) { + this._selectedCell[1] = Math.min(this._selectedCell[1] + 1, this.columnKeys.length - 1); + } else if (this._selectedDocs.length > 0) { + this.selectCell(this._selectedDocs[0], 0); + } + break; + case 'ArrowLeft': + if (this._selectedCell) { + this._selectedCell[1] = Math.max(this._selectedCell[1] - 1, 0); + } else if (this._selectedDocs.length > 0) { + this.selectCell(this._selectedDocs[0], 0); + } + break; + case 'Backspace': { + this.removeDocument(this._selectedDocs); + break; + } + case 'Escape': { + this._selectedCell = undefined; + } } } }; @@ -392,6 +420,11 @@ export class CollectionSchemaView extends CollectionSubView() { } }; + @action + selectCell = (doc: Doc, index: number) => { + this._selectedCell = [doc, index]; + }; + sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc)); setDropIndex = (index: number) => (this._closestDropIndex = index); @@ -428,7 +461,7 @@ export class CollectionSchemaView extends CollectionSubView() { const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs]; const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc)); this.dataDoc[this.fieldKey ?? 'data'] = new List([...removed, ...draggedDocs, ...pushedDocs]); - SelectionManager.DeselectAll(); + this.clearSelection(); draggedDocs.forEach(doc => { const draggedView = DocumentManager.Instance.getFirstDocumentView(doc); if (draggedView) DocumentManager.Instance.RemoveView(draggedView); diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 79808d8f8..f5a16cec0 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -123,6 +123,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { { - get readOnly() { + private _editorRef: EditableView | null = null; + + @computed get readOnly() { return this.props.schemaView?.fieldInfos[this.props.fieldKey]?.readOnly ?? false; } + @computed get selected() { + const selected: [Doc, number] | undefined = this.props.schemaView?._selectedCell; + return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col; + } + + componentDidUpdate() { + if (!this.selected) { + this._editorRef?.setIsFocused(false); + } + } + get defaultCellContent() { const props: FieldViewProps = { Document: this.props.Document, @@ -60,6 +76,7 @@ export class SchemaTableCell extends React.Component { return (
(this._editorRef = ref)} contents={} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { @@ -68,20 +85,12 @@ export class SchemaTableCell extends React.Component { } return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); }} - editing={this.props.isRowActive() ? undefined : false} + editing={this.selected ? undefined : false} />
); } - getCellWithContent(content: any) { - return ( -
- {content} -
- ); - } - get getCellType() { const columnTypeStr = this.props.schemaView?.fieldInfos[this.props.fieldKey]?.fieldType; if (columnTypeStr) { @@ -99,7 +108,7 @@ export class SchemaTableCell extends React.Component { return ColumnType.Any; } - render() { + get content() { const cellType: ColumnType = this.getCellType; switch (cellType) { case ColumnType.Image: @@ -107,14 +116,27 @@ export class SchemaTableCell extends React.Component { case ColumnType.Date: // return ; default: - return this.getCellWithContent(this.defaultCellContent); + return this.defaultCellContent; } } + + render() { + return ( +
{ + if (!this.selected) this.props.schemaView?.selectCell(this.props.Document, this.props.col); + })} + style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> + {this.content} +
+ ); + } } // mj: most of this is adapted from old schema code so I'm not sure what it does tbh @observer -export class SchemaImageCell extends SchemaTableCell { +export class SchemaImageCell extends React.Component { @observable _previewRef: HTMLImageElement | undefined; choosePath(url: URL) { @@ -164,7 +186,7 @@ export class SchemaImageCell extends SchemaTableCell { document.body.removeChild(this._previewRef); }; - get content() { + render() { const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px @@ -173,14 +195,10 @@ export class SchemaImageCell extends SchemaTableCell { return ; } - - render() { - return this.getCellWithContent(this.content); - } } @observer -export class SchemaDateCell extends SchemaTableCell { +export class SchemaDateCell extends React.Component { @observable _pickingDate: boolean = false; @computed get date(): DateField { @@ -199,12 +217,7 @@ export class SchemaDateCell extends SchemaTableCell { //} }; - get content() { - return this.handleChange(date)} />; - // return !this._pickingDate ?
(this._pickingDate = true))}>{this.defaultCellContent}
: this.handleChange(date)} />; - } - render() { - return this.getCellWithContent(this.content); + return this.handleChange(date)} />; } } -- cgit v1.2.3-70-g09d2 From b82a909a63a6de414d075735453240ebc02f5aa3 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 27 Apr 2023 13:20:52 -0400 Subject: fixed editing schema cells with keyboard only --- src/client/views/EditableView.tsx | 6 +++++ .../collectionSchema/CollectionSchemaView.tsx | 19 +++++++++------ .../collections/collectionSchema/SchemaRowBox.tsx | 6 +++-- .../collectionSchema/SchemaTableCell.tsx | 28 ++++++++++++++++------ 4 files changed, 43 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 164b6c57a..d1311a60a 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -106,6 +106,12 @@ export class EditableView extends React.Component { case ':': this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y); break; + case 'ArrowUp': + case 'ArrowDown': + case 'ArrowLeft': + case 'ArrowRight': + e.stopPropagation(); + break; case 'Shift': case 'Alt': case 'Meta': diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 92a04f5ec..8cd307adf 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,6 +1,6 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, ObservableMap, untracked } from 'mobx'; +import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc'; @@ -170,12 +170,12 @@ export class CollectionSchemaView extends CollectionSubView() { const lastDoc = this._selectedDocs.lastElement(); const lastIndex = this.rowIndex(lastDoc); const curDoc = this.sortedDocs.docs[lastIndex]; - // const curDoc = this.childDocs[lastIndex]; if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[lastIndex + 1]; - if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); - else { + if (this._selectedDocs.includes(newDoc)) { + SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); + } else { this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1); this._selectedCell && (this._selectedCell[0] = newDoc); this.scrollToDoc(newDoc, {}); @@ -189,7 +189,6 @@ export class CollectionSchemaView extends CollectionSubView() { { const firstDoc = this._selectedDocs.lastElement(); const firstIndex = this.rowIndex(firstDoc); - // const curDoc = this.childDocs[firstIndex]; const curDoc = this.sortedDocs.docs[firstIndex]; if (firstIndex > 0 && firstIndex < this.childDocs.length) { !e.shiftKey && this.clearSelection(); @@ -224,7 +223,7 @@ export class CollectionSchemaView extends CollectionSubView() { break; } case 'Escape': { - this._selectedCell = undefined; + this.deselectCell(); } } } @@ -425,6 +424,11 @@ export class CollectionSchemaView extends CollectionSubView() { this._selectedCell = [doc, index]; }; + @action + deselectCell = () => { + this._selectedCell = undefined; + }; + sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc)); setDropIndex = (index: number) => (this._closestDropIndex = index); @@ -456,7 +460,7 @@ export class CollectionSchemaView extends CollectionSubView() { return true; } const draggedDocs = de.complete.docDragData?.draggedDocuments; - if (draggedDocs && super.onInternalDrop(e, de)) { + if (draggedDocs && super.onInternalDrop(e, de) && !this.sortField) { const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc)); const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs]; const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc)); @@ -829,6 +833,7 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get sortedDocs() { + trace(); const field = StrCast(this.layoutDoc.sortField); const desc = BoolCast(this.layoutDoc.sortDesc); const docs = !field diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index f5a16cec0..9864820a3 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -28,6 +28,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined; } + schemaViewFunc = () => this.schemaView; + @computed get schemaDoc() { return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; } @@ -124,11 +126,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { key={key} Document={this.rootDoc} col={index} - schemaView={this.schemaView} + schemaView={this.schemaViewFunc} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} isRowActive={this.props.isContentActive} - setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false} + // setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false} /> ))}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 2b61ea261..374b92d72 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -21,11 +21,11 @@ import { SelectionManager } from '../../../util/SelectionManager'; export interface SchemaTableCellProps { Document: Doc; col: number; - schemaView: CollectionSchemaView | undefined; + schemaView: () => CollectionSchemaView | undefined; fieldKey: string; columnWidth: number; isRowActive: () => boolean | undefined; - setColumnValues: (field: string, value: string) => boolean; + // setColumnValues: (field: string, value: string) => boolean; } @observer @@ -33,20 +33,34 @@ export class SchemaTableCell extends React.Component { private _editorRef: EditableView | null = null; @computed get readOnly() { - return this.props.schemaView?.fieldInfos[this.props.fieldKey]?.readOnly ?? false; + return this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.readOnly ?? false; } @computed get selected() { - const selected: [Doc, number] | undefined = this.props.schemaView?._selectedCell; + const selected: [Doc, number] | undefined = this.props.schemaView()?._selectedCell; return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col; } componentDidUpdate() { if (!this.selected) { this._editorRef?.setIsFocused(false); + document.removeEventListener('keydown', this.onKeyDown); + } else if (!this.readOnly) { + document.addEventListener('keydown', this.onKeyDown); } } + @action + onKeyDown = (e: KeyboardEvent) => { + e.stopPropagation(); + if (e.key == 'Enter') { + this._editorRef?.setIsFocused(true); + } + if (e.key == 'Escape') { + this.props.schemaView()?.deselectCell(); + } + }; + get defaultCellContent() { const props: FieldViewProps = { Document: this.props.Document, @@ -81,7 +95,7 @@ export class SchemaTableCell extends React.Component { GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { - this.props.setColumnValues(this.props.fieldKey, value); + // this.props.setColumnValues(this.props.fieldKey, value); } return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); }} @@ -92,7 +106,7 @@ export class SchemaTableCell extends React.Component { } get getCellType() { - const columnTypeStr = this.props.schemaView?.fieldInfos[this.props.fieldKey]?.fieldType; + const columnTypeStr = this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.fieldType; if (columnTypeStr) { if (columnTypeStr in FInfotoColType) { return FInfotoColType[columnTypeStr]; @@ -125,7 +139,7 @@ export class SchemaTableCell extends React.Component {
{ - if (!this.selected) this.props.schemaView?.selectCell(this.props.Document, this.props.col); + if (!this.selected) this.props.schemaView()?.selectCell(this.props.Document, this.props.col); })} style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content} -- cgit v1.2.3-70-g09d2 From ba5b687011526188bb024ddf37c254aaf285c06d Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 16:45:59 -0400 Subject: performance tuning for schema views to avoid re-rendering each table cell when child docs change. --- .../collectionLinear/CollectionLinearView.tsx | 20 ++-- .../collectionSchema/CollectionSchemaView.tsx | 105 +++++++++------------ .../collections/collectionSchema/SchemaRowBox.tsx | 19 ++-- .../collectionSchema/SchemaTableCell.tsx | 48 +++++----- 4 files changed, 94 insertions(+), 98 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 97eed7752..efd73a927 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -255,15 +255,17 @@ export class CollectionLinearView extends CollectionSubView() { })} /> -
- {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))} -
+ {!this.layoutDoc.linearViewIsExpanded ? null : ( +
+ {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))} +
+ )}
); diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 8cd307adf..50a91a60a 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,9 +1,9 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx'; +import { action, computed, observable, ObservableMap, observe, reaction, trace, untracked } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; -import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, NumListCast, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; @@ -47,6 +47,7 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate', @observer export class CollectionSchemaView extends CollectionSubView() { + private _keysDisposer: any; private _closestDropIndex: number = 0; private _previewRef: HTMLDivElement | null = null; private _makeNewColumn: boolean = false; @@ -58,15 +59,13 @@ export class CollectionSchemaView extends CollectionSubView() { public static _rowMenuWidth: number = 60; public static _previewDividerWidth: number = 4; public static _newNodeInputHeight: number = 30; + public fieldInfos = new ObservableMap(); - @computed get _selectedDocs() { - return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document)); - } + @observable _menuKeys: string[] = []; @observable _rowEles: ObservableMap = new ObservableMap(); @observable _colEles: HTMLDivElement[] = []; @observable _displayColumnWidths: number[] | undefined; @observable _columnMenuIndex: number | undefined; - @observable _fieldInfos: [string, FInfo][] = []; @observable _newFieldWarning: string = ''; @observable _makeNewField: boolean = false; @observable _newFieldDefault: any = 0; @@ -76,34 +75,12 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _filterSearchValue: string = ''; @observable _selectedCell: [Doc, number] | undefined; - get fieldInfos() { - const docs = this.childDocs; - const keys: { [key: string]: boolean } = {}; - // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. - // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be - // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. - // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu - // is displayed (unlikely) it won't show up until something else changes. - //TODO Types - untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))))); - - let computedKeys: { [key: string]: FInfo } = {}; - - Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => { - computedKeys[pair[0]] = pair[1]; - }); - - Object.keys(keys).forEach((key: string) => { - if (!(key in computedKeys)) { - computedKeys[key] = new FInfo(''); - } - }); - - return computedKeys; + @computed get _selectedDocs() { + return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.rootDoc)); } - get documentKeys() { - return Object.keys(this.fieldInfos); + @computed get documentKeys() { + return Array.from(this.fieldInfos.keys()); } @computed get previewWidth() { @@ -119,20 +96,15 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get storedColumnWidths() { - let widths = Cast( + const widths = NumListCast( this.layoutDoc.columnWidths, - listSpec('number'), this.columnKeys.map(() => (this.tableWidth - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length) ); const totalWidth = widths.reduce((sum, width) => sum + width, 0); if (totalWidth !== this.tableWidth - CollectionSchemaView._rowMenuWidth) { - widths = widths.map(w => { - const proportion = w / totalWidth; - return proportion * (this.tableWidth - CollectionSchemaView._rowMenuWidth); - }); + return widths.map(w => (w / totalWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)); } - return widths; } @@ -148,19 +120,37 @@ export class CollectionSchemaView extends CollectionSubView() { return BoolCast(this.layoutDoc.sortDesc); } - rowIndex(doc: Doc) { - return this.sortedDocs.docs.indexOf(doc); - } - + @action componentDidMount() { this.props.setContentView?.(this); document.addEventListener('keydown', this.onKeyDown); + + Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1])); + this._keysDisposer = observe( + this.rootDoc[this.fieldKey ?? 'data'] as List, + change => { + switch (change.type as any) { + case 'splice': + // prettier-ignore + (change as any).added.forEach((doc: Doc) => // for each document added + Doc.GetAllPrototypes(doc).forEach(proto => // for all of its prototypes (and itself) + Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them + !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo('')))))); + break; + case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list + } + }, + true + ); } componentWillUnmount() { + this._keysDisposer?.(); document.removeEventListener('keydown', this.onKeyDown); } + rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc); + @action onKeyDown = (e: KeyboardEvent) => { if (this._selectedDocs.length > 0) { @@ -554,8 +544,7 @@ export class CollectionSchemaView extends CollectionSubView() { onSearchKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Enter': - const menuKeys = Object.keys(this._fieldInfos); - menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(menuKeys[0]) : action(() => (this._makeNewField = true))(); + this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))(); break; case 'Escape': this.closeColumnMenu(); @@ -584,7 +573,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._makeNewColumn = false; this._columnMenuIndex = index; this._menuValue = ''; - this._fieldInfos = Object.entries(this.fieldInfos); + this._menuKeys = this.documentKeys; this._makeNewField = false; this._newFieldWarning = ''; this._makeNewField = false; @@ -630,7 +619,7 @@ export class CollectionSchemaView extends CollectionSubView() { @action updateKeySearch = (e: React.ChangeEvent) => { this._menuValue = e.target.value; - this._fieldInfos = Object.entries(this.fieldInfos).filter(value => value[0].toLowerCase().includes(this._menuValue.toLowerCase())); + this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase())); }; getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); @@ -732,7 +721,7 @@ export class CollectionSchemaView extends CollectionSubView() { { passive: false } ) }> - {this._fieldInfos.map(([key, info]) => ( + {this._menuKeys.map(key => (
{ @@ -742,13 +731,13 @@ export class CollectionSchemaView extends CollectionSubView() {

{key} - {info.fieldType ? ', ' : ''} + {this.fieldInfos.get(key)!.fieldType ? ', ' : ''} - - {info.fieldType} + + {this.fieldInfos.get(key)!.fieldType}

-

{info.description}

+

{this.fieldInfos.get(key)!.description}

))}
@@ -888,13 +877,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{this._columnMenuIndex !== undefined && this.renderColumnMenu} {this._filterColumnIndex !== undefined && this.renderFilterMenu} - { - this._tableContentRef = ref; - }} - /> + (this._tableContentRef = ref)} />
{this.previewWidth > 0 &&
} @@ -923,7 +906,7 @@ export class CollectionSchemaView extends CollectionSubView() { moveDocument={this.props.moveDocument} addDocument={this.addRow} removeDocument={this.props.removeDocument} - whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} + whenChildContentsActiveChanged={returnFalse} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} @@ -978,7 +961,7 @@ class CollectionSchemaViewDocs extends React.Component this.props.schema.props.whenChildContentsActiveChanged(active)} + whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged} hideDecorations={true} hideTitle={true} hideDocumentButtonBar={true} diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 9864820a3..9772ce118 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -12,6 +12,8 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { CollectionSchemaView } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { SchemaTableCell } from './SchemaTableCell'; +import { computedFn } from 'mobx-utils'; +import { Doc } from '../../../../fields/Doc'; @observer export class SchemaRowBox extends ViewBoxBaseComponent() { @@ -24,12 +26,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { bounds = () => this._ref?.getBoundingClientRect(); @computed get schemaView() { - const vpath = this.props.docViewPath(); - return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined; + return this.props.DocumentView?.().props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView; } - schemaViewFunc = () => this.schemaView; - @computed get schemaDoc() { return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; } @@ -86,6 +85,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { document.removeEventListener('pointermove', this.onPointerMove); }; + getFinfo = computedFn((fieldKey: string) => this.schemaView?.fieldInfos.get(fieldKey)); + selectCell = (doc: Doc, col: number) => this.schemaView?.selectCell(doc, col); + deselectCell = () => this.schemaView?.deselectCell(); + selectedCell = () => this.schemaView?._selectedCell; + setColumnValues = (field: any, value: any) => this.schemaView?.setColumnValues(field, value) ?? false; render() { return (
() { key={key} Document={this.rootDoc} col={index} - schemaView={this.schemaViewFunc} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} isRowActive={this.props.isContentActive} - // setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false} + getFinfo={this.getFinfo} + selectCell={this.selectCell} + deselectCell={this.deselectCell} + selectedCell={this.selectedCell} + setColumnValues={this.setColumnValues} /> ))}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 374b92d72..686b21283 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,31 +1,34 @@ import React = require('react'); +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; -import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; +import { extname } from 'path'; +import DatePicker from 'react-datepicker'; +import { DateField } from '../../../../fields/DateField'; +import { Doc, DocListCast, Field } from '../../../../fields/Doc'; +import { Cast, DateCast } from '../../../../fields/Types'; +import { ImageField } from '../../../../fields/URLField'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils'; import { Transform } from '../../../util/Transform'; import { EditableView } from '../../EditableView'; +import { Colors } from '../../global/globalEnums'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { DefaultStyleProvider } from '../../StyleProvider'; import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; -import { action, computed, observable } from 'mobx'; -import { extname } from 'path'; -import { Cast, DateCast, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; -import { DateField } from '../../../../fields/DateField'; -import DatePicker from 'react-datepicker'; -import { Colors } from '../../global/globalEnums'; -import { SelectionManager } from '../../../util/SelectionManager'; +import { FInfo } from '../../../documents/Documents'; export interface SchemaTableCellProps { Document: Doc; col: number; - schemaView: () => CollectionSchemaView | undefined; + deselectCell: () => void; + selectCell: (doc: Doc, col: number) => void; + selectedCell: () => [Doc, number] | undefined; fieldKey: string; columnWidth: number; isRowActive: () => boolean | undefined; - // setColumnValues: (field: string, value: string) => boolean; + getFinfo: (fieldKey: string) => FInfo | undefined; + setColumnValues: (field: string, value: string) => boolean; } @observer @@ -33,11 +36,11 @@ export class SchemaTableCell extends React.Component { private _editorRef: EditableView | null = null; @computed get readOnly() { - return this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.readOnly ?? false; + return this.props.getFinfo(this.props.fieldKey)?.readOnly ?? false; } @computed get selected() { - const selected: [Doc, number] | undefined = this.props.schemaView()?._selectedCell; + const selected: [Doc, number] | undefined = this.props.selectedCell(); return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col; } @@ -57,11 +60,12 @@ export class SchemaTableCell extends React.Component { this._editorRef?.setIsFocused(true); } if (e.key == 'Escape') { - this.props.schemaView()?.deselectCell(); + this.props.deselectCell(); } }; - - get defaultCellContent() { + colWidthFunc = () => this.props.columnWidth; + colRowHeightFunc = () => CollectionSchemaView._rowHeight; + @computed get defaultCellContent() { const props: FieldViewProps = { Document: this.props.Document, docFilters: returnEmptyFilter, @@ -81,8 +85,8 @@ export class SchemaTableCell extends React.Component { whenChildContentsActiveChanged: emptyFunction, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, - PanelWidth: () => this.props.columnWidth, - PanelHeight: () => CollectionSchemaView._rowHeight, + PanelWidth: this.colWidthFunc, + PanelHeight: this.colRowHeightFunc, addDocTab: returnFalse, pinToPres: returnZero, }; @@ -95,7 +99,7 @@ export class SchemaTableCell extends React.Component { GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { - // this.props.setColumnValues(this.props.fieldKey, value); + this.props.setColumnValues(this.props.fieldKey, value); } return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); }} @@ -106,7 +110,7 @@ export class SchemaTableCell extends React.Component { } get getCellType() { - const columnTypeStr = this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.fieldType; + const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType; if (columnTypeStr) { if (columnTypeStr in FInfotoColType) { return FInfotoColType[columnTypeStr]; @@ -139,7 +143,7 @@ export class SchemaTableCell extends React.Component {
{ - if (!this.selected) this.props.schemaView()?.selectCell(this.props.Document, this.props.col); + if (!this.selected) this.props.selectCell(this.props.Document, this.props.col); })} style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content} -- cgit v1.2.3-70-g09d2 From 08d94147eb855bbb3d7eb964ffa6a7a3248001a2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 20:41:14 -0400 Subject: forced 'hidden' documents to appear in schema. added colorizing/underlining to schema cells to indicate if value is on layout, data, or proto --- src/client/views/StyleProvider.tsx | 3 ++- .../collections/collectionSchema/SchemaTableCell.tsx | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 5f16e0ebd..c810cb155 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -24,6 +24,7 @@ import { KeyValueBox } from './nodes/KeyValueBox'; import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); +import { SchemaRowBox } from './collections/collectionSchema/SchemaRowBox'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -173,7 +174,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { addDocTab: returnFalse, pinToPres: returnZero, }; + let protoCount = 0; + let doc: Doc | undefined = this.props.Document; + while (doc) { + if (Object.keys(doc).includes(this.props.fieldKey)) { + break; + } + protoCount++; + doc = doc.proto; + } + const parenCount = Math.max(0, protoCount - 1); + const color = protoCount === 0 ? 'black' : 'blue'; return ( -
+
(this._editorRef = ref)} contents={} -- cgit v1.2.3-70-g09d2 From cc62b9949854f27bc19eb8d4224456094f0dbe13 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 20:48:27 -0400 Subject: from last --- src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index bbf8e2bbf..4f4986b90 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -93,7 +93,7 @@ export class SchemaTableCell extends React.Component { let protoCount = 0; let doc: Doc | undefined = this.props.Document; while (doc) { - if (Object.keys(doc).includes(this.props.fieldKey)) { + if (Object.keys(doc).includes(this.props.fieldKey.replace(/^_/, ''))) { break; } protoCount++; -- cgit v1.2.3-70-g09d2 From 223dde9f35408c229a4da583083d10cbf81fc264 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 22:23:42 -0400 Subject: fixed some undo issues with schema fill down. added bool cell view. added UI for showing/setting values on layout vs data docs. --- .../collections/collectionSchema/SchemaRowBox.tsx | 3 +- .../collectionSchema/SchemaTableCell.tsx | 156 ++++++++++++--------- src/client/views/nodes/KeyValueBox.tsx | 2 - 3 files changed, 89 insertions(+), 72 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 9772ce118..ca9e0bda0 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -90,6 +90,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { deselectCell = () => this.schemaView?.deselectCell(); selectedCell = () => this.schemaView?._selectedCell; setColumnValues = (field: any, value: any) => this.schemaView?.setColumnValues(field, value) ?? false; + columnWidth = computedFn((index: number) => () => this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth); render() { return (
() { Document={this.rootDoc} col={index} fieldKey={key} - columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} + columnWidth={this.columnWidth(index)} isRowActive={this.props.isContentActive} getFinfo={this.getFinfo} selectCell={this.selectCell} diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 4f4986b90..1fa4312e1 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -5,10 +5,13 @@ import { extname } from 'path'; import DatePicker from 'react-datepicker'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field } from '../../../../fields/Doc'; -import { Cast, DateCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DateCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils'; +import { FInfo } from '../../../documents/Documents'; +import { dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; +import { undoBatch } from '../../../util/UndoManager'; import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; @@ -16,7 +19,6 @@ import { KeyValueBox } from '../../nodes/KeyValueBox'; import { DefaultStyleProvider } from '../../StyleProvider'; import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; -import { FInfo } from '../../../documents/Documents'; export interface SchemaTableCellProps { Document: Doc; @@ -25,7 +27,7 @@ export interface SchemaTableCellProps { selectCell: (doc: Doc, col: number) => void; selectedCell: () => [Doc, number] | undefined; fieldKey: string; - columnWidth: number; + columnWidth: () => number; isRowActive: () => boolean | undefined; getFinfo: (fieldKey: string) => FInfo | undefined; setColumnValues: (field: string, value: string) => boolean; @@ -33,95 +35,78 @@ export interface SchemaTableCellProps { @observer export class SchemaTableCell extends React.Component { - private _editorRef: EditableView | null = null; - - @computed get readOnly() { - return this.props.getFinfo(this.props.fieldKey)?.readOnly ?? false; - } - - @computed get selected() { - const selected: [Doc, number] | undefined = this.props.selectedCell(); - return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col; + public static colRowHeightFunc() { + return CollectionSchemaView._rowHeight; } - - componentDidUpdate() { - if (!this.selected) { - this._editorRef?.setIsFocused(false); - document.removeEventListener('keydown', this.onKeyDown); - } else if (!this.readOnly) { - document.addEventListener('keydown', this.onKeyDown); - } - } - - @action - onKeyDown = (e: KeyboardEvent) => { - e.stopPropagation(); - if (e.key == 'Enter') { - this._editorRef?.setIsFocused(true); - } - if (e.key == 'Escape') { - this.props.deselectCell(); + public static renderProps(props: SchemaTableCellProps) { + const { Document, fieldKey, getFinfo, columnWidth, isRowActive } = props; + let protoCount = 0; + let doc: Doc | undefined = Document; + while (doc) { + if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) { + break; + } + protoCount++; + doc = doc.proto; } - }; - colWidthFunc = () => this.props.columnWidth; - colRowHeightFunc = () => CollectionSchemaView._rowHeight; - @computed get defaultCellContent() { - const props: FieldViewProps = { - Document: this.props.Document, + const parenCount = Math.max(0, protoCount - 1); + const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; + const textDecoration = color !== 'black' && parenCount ? 'underline' : ''; + const fieldProps: FieldViewProps = { docFilters: returnEmptyFilter, docRangeFilters: returnEmptyFilter, searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, - fieldKey: this.props.fieldKey, rootSelected: returnFalse, isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, - dropAction: 'alias', + dropAction: 'alias' as dropActionType, bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, whenChildContentsActiveChanged: emptyFunction, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, - PanelWidth: this.colWidthFunc, - PanelHeight: this.colRowHeightFunc, addDocTab: returnFalse, pinToPres: returnZero, + Document, + fieldKey, + PanelWidth: columnWidth, + PanelHeight: SchemaTableCell.colRowHeightFunc, }; - let protoCount = 0; - let doc: Doc | undefined = this.props.Document; - while (doc) { - if (Object.keys(doc).includes(this.props.fieldKey.replace(/^_/, ''))) { - break; - } - protoCount++; - doc = doc.proto; - } - const parenCount = Math.max(0, protoCount - 1); - const color = protoCount === 0 ? 'black' : 'blue'; + const readOnly = getFinfo(fieldKey)?.readOnly ?? false; + const cursor = !readOnly ? 'text' : 'default'; + const pointerEvents: 'all' | 'none' = !readOnly && isRowActive() ? 'all' : 'none'; + return { color, textDecoration, fieldProps, cursor, pointerEvents }; + } + + @computed get selected() { + const selected: [Doc, number] | undefined = this.props.selectedCell(); + return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + } + + @computed get defaultCellContent() { + const { color, textDecoration, fieldProps } = SchemaTableCell.renderProps(this.props); return (
(this._editorRef = ref)} - contents={} + contents={} + editing={this.selected ? undefined : false} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} - SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { + SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { this.props.setColumnValues(this.props.fieldKey, value); } return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); - }} - editing={this.selected ? undefined : false} + })} />
); @@ -146,13 +131,12 @@ export class SchemaTableCell extends React.Component { get content() { const cellType: ColumnType = this.getCellType; + // prettier-ignore switch (cellType) { - case ColumnType.Image: - return ; - case ColumnType.Date: - // return ; - default: - return this.defaultCellContent; + case ColumnType.Image: return ; + case ColumnType.Boolean: return ; + case ColumnType.Date: // return ; + default: return this.defaultCellContent; } } @@ -160,10 +144,8 @@ export class SchemaTableCell extends React.Component { return (
{ - if (!this.selected) this.props.selectCell(this.props.Document, this.props.col); - })} - style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> + onPointerDown={action(e => !this.selected && this.props.selectCell(this.props.Document, this.props.col))} + style={{ width: this.props.columnWidth(), border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content}
); @@ -257,3 +239,39 @@ export class SchemaDateCell extends React.Component { return this.handleChange(date)} />; } } +@observer +export class SchemaBoolCell extends React.Component { + @computed get selected() { + const selected: [Doc, number] | undefined = this.props.selectedCell(); + return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + } + render() { + const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props); + return ( +
+ | undefined) => { + if ((value?.nativeEvent as any).shiftKey) { + this.props.setColumnValues(this.props.fieldKey, (color === 'black' ? '=' : '') + value?.target?.checked.toString()); + } + KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); + })} + /> + } + editing={this.selected ? undefined : false} + GetValue={() => (color === 'black' ? '=' : '') + Field.toKeyValueString(this.props.Document, this.props.fieldKey)} + SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { + if (shiftDown && enterKey) { + this.props.setColumnValues(this.props.fieldKey, value); + } + return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value); + })} + /> +
+ ); + } +} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 57018fb93..11220c300 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -20,14 +20,12 @@ import { ImageBox } from './ImageBox'; import './KeyValueBox.scss'; import { KeyValuePair } from './KeyValuePair'; import React = require('react'); -import e = require('express'); export type KVPScript = { script: CompiledScript; type: 'computed' | 'script' | false; onDelegate: boolean; }; - @observer export class KeyValueBox extends React.Component { public static LayoutString() { -- cgit v1.2.3-70-g09d2 From 773164e71dd6420d5ee669b138f2b6ba05164874 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 22:38:59 -0400 Subject: from last - fix for fill down to write to layout /data doc properly. --- src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 1fa4312e1..003831094 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -103,9 +103,9 @@ export class SchemaTableCell extends React.Component { GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { - this.props.setColumnValues(this.props.fieldKey, value); + this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value); } - return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); + return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value); })} />
@@ -255,7 +255,7 @@ export class SchemaBoolCell extends React.Component { checked={BoolCast(this.props.Document[this.props.fieldKey])} onChange={undoBatch((value: React.ChangeEvent | undefined) => { if ((value?.nativeEvent as any).shiftKey) { - this.props.setColumnValues(this.props.fieldKey, (color === 'black' ? '=' : '') + value?.target?.checked.toString()); + this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); } KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString()); })} @@ -266,7 +266,7 @@ export class SchemaBoolCell extends React.Component { GetValue={() => (color === 'black' ? '=' : '') + Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { - this.props.setColumnValues(this.props.fieldKey, value); + this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value); } return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value); })} -- cgit v1.2.3-70-g09d2 From d3dc9938b38e89b2215d13fbc5bc92d33502e818 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 27 Apr 2023 22:53:45 -0400 Subject: one more fix to setting on data/layout for undefined values on '_' fields. --- src/client/views/collections/collectionSchema/SchemaTableCell.tsx | 2 +- src/fields/Doc.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 003831094..f17f4a73c 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -263,7 +263,7 @@ export class SchemaBoolCell extends React.Component { } editing={this.selected ? undefined : false} - GetValue={() => (color === 'black' ? '=' : '') + Field.toKeyValueString(this.props.Document, this.props.fieldKey)} + GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index b0033b977..22d0664ce 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -29,9 +29,13 @@ import JSZip = require('jszip'); import * as JSZipUtils from '../JSZipUtils'; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { - const onDelegate = Object.keys(doc).includes(key); + const onDelegate = Object.keys(doc).includes(key.replace(/^_/, '')); const field = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : field instanceof ScriptField ? `$=${field.script.originalScript}` : Field.toScriptString(field)); + return !Field.IsField(field) + ? key.startsWith('_') + ? '=' + : '' + : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : field instanceof ScriptField ? `$=${field.script.originalScript}` : Field.toScriptString(field)); } export function toScriptString(field: Field): string { switch (typeof field) { -- cgit v1.2.3-70-g09d2 From becc884883df07635f4619f908747598b3eb86ee Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 28 Apr 2023 09:57:53 -0400 Subject: added RTF cell type for schema. fixed formatting DateFields so that they can be set from kvp/schema. prevented on infinite loop possibility when setting proto to itself. --- src/Utils.ts | 3 ++ .../collectionSchema/CollectionSchemaView.tsx | 15 ++++---- .../collectionSchema/SchemaTableCell.tsx | 40 ++++++++++++++++------ src/client/views/nodes/KeyValueBox.tsx | 2 +- src/fields/DateField.ts | 14 ++++---- 5 files changed, 48 insertions(+), 26 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 0c7deaf5d..73de6d754 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -513,6 +513,9 @@ export function returnTrue() { return true; } +export function returnIgnore(): 'ignore' { + return 'ignore'; +} export function returnAlways(): 'always' { return 'always'; } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 3f7e037d4..a59d7e5a3 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -8,7 +8,7 @@ import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils'; +import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils'; import { Docs, DocumentOptions, DocUtils, FInfo } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -32,6 +32,7 @@ export enum ColumnType { Boolean, Date, Image, + RTF, Any, } @@ -41,6 +42,7 @@ export const FInfotoColType: { [key: string]: ColumnType } = { boolean: ColumnType.Boolean, date: ColumnType.Date, image: ColumnType.Image, + rtf: ColumnType.RTF, }; const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate', 'text']; @@ -410,14 +412,10 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - selectCell = (doc: Doc, index: number) => { - this._selectedCell = [doc, index]; - }; + selectCell = (doc: Doc, index: number) => (this._selectedCell = [doc, index]); @action - deselectCell = () => { - this._selectedCell = undefined; - }; + deselectCell = () => (this._selectedCell = undefined); sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc)); @@ -891,6 +889,7 @@ export class CollectionSchemaView extends CollectionSubView() { dontCenter={'y'} onClickScriptDisable="always" focus={emptyFunction} + defaultDoubleClick={returnIgnore} renderDepth={this.props.renderDepth + 1} rootSelected={this.rootSelected} PanelWidth={this.previewWidthFunc} @@ -949,7 +948,7 @@ class CollectionSchemaViewDocs extends React.Component { } get getCellType() { - const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType; - if (columnTypeStr) { - if (columnTypeStr in FInfotoColType) { - return FInfotoColType[columnTypeStr]; - } - - return ColumnType.Any; - } - const cellValue = this.props.Document[this.props.fieldKey]; if (cellValue instanceof ImageField) return ColumnType.Image; if (cellValue instanceof DateField) return ColumnType.Date; + if (cellValue instanceof RichTextField) return ColumnType.RTF; + if (typeof cellValue === 'number') return ColumnType.Any; + if (typeof cellValue === 'string') return ColumnType.Any; + if (typeof cellValue === 'boolean') return ColumnType.Any; + + const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType; + if (columnTypeStr && columnTypeStr in FInfotoColType) { + return FInfotoColType[columnTypeStr]; + } return ColumnType.Any; } @@ -135,6 +137,7 @@ export class SchemaTableCell extends React.Component { switch (cellType) { case ColumnType.Image: return ; case ColumnType.Boolean: return ; + case ColumnType.RTF: return ; case ColumnType.Date: // return ; default: return this.defaultCellContent; } @@ -240,6 +243,23 @@ export class SchemaDateCell extends React.Component { } } @observer +export class SchemaRTFCell extends React.Component { + @computed get selected() { + const selected: [Doc, number] | undefined = this.props.selectedCell(); + return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col; + } + selectedFunc = () => this.selected; + render() { + const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props); + fieldProps.isContentActive = this.selectedFunc; + return ( +
+ {this.selected ? : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} +
+ ); + } +} +@observer export class SchemaBoolCell extends React.Component { @computed get selected() { const selected: [Doc, number] | undefined = this.props.selectedCell(); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index b54364332..e317de11e 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -96,7 +96,7 @@ export class KeyValueBox extends React.Component { } field = res.result; } - if (Field.IsField(field, true)) { + if (Field.IsField(field, true) && (key !== 'proto' || field !== target)) { target[key] = field; return true; } diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts index 26f51b2d3..2ea619bd9 100644 --- a/src/fields/DateField.ts +++ b/src/fields/DateField.ts @@ -1,11 +1,11 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, date } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; -import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals"; +import { Deserializable } from '../client/util/SerializationHelper'; +import { serializable, date } from 'serializr'; +import { ObjectField } from './ObjectField'; +import { Copy, ToScriptString, ToString } from './FieldSymbols'; +import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals'; @scriptingGlobal -@Deserializable("date") +@Deserializable('date') export class DateField extends ObjectField { @serializable(date()) readonly date: Date; @@ -24,7 +24,7 @@ export class DateField extends ObjectField { } [ToScriptString]() { - return `new DateField(new Date(${this.date.toISOString()}))`; + return `new DateField(new Date("${this.date.toISOString()}"))`; } [ToString]() { return this.date.toLocaleString(); -- cgit v1.2.3-70-g09d2 From dca61505ba138eef3819b16b760ec81becf9329e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 13 May 2023 09:55:48 -0400 Subject: changed EditableViews to support oneline and multiline. Also added transformer UI to allow documents to be entered. changed transformer to write doc id's, not variables.. made schema view support oneline and fixed bug with docdecoration hader occluding things invisibly. updated web pages to be zoomable and for its anchors to update web page and scroll location properly. made autolinkanchor directly go to target on click. --- package-lock.json | 39 +++ src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 5 +- src/client/util/DocumentManager.ts | 24 +- src/client/util/Scripting.ts | 30 +- src/client/views/DocumentDecorations.tsx | 8 +- src/client/views/EditableView.scss | 11 +- src/client/views/EditableView.tsx | 98 ++++-- src/client/views/MarqueeAnnotator.tsx | 3 +- src/client/views/ScriptingRepl.scss | 5 +- src/client/views/ScriptingRepl.tsx | 113 ++++--- .../views/collections/CollectionStackingView.scss | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 +- .../collectionSchema/CollectionSchemaView.scss | 4 +- .../collectionSchema/CollectionSchemaView.tsx | 23 +- .../collectionSchema/SchemaColumnHeader.tsx | 1 + .../collections/collectionSchema/SchemaRowBox.tsx | 10 +- .../collectionSchema/SchemaTableCell.tsx | 44 ++- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- src/client/views/nodes/DocumentIcon.tsx | 56 ++-- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/KeyValueBox.tsx | 6 +- src/client/views/nodes/KeyValuePair.scss | 64 ++-- src/client/views/nodes/KeyValuePair.tsx | 8 +- src/client/views/nodes/WebBox.tsx | 359 ++++++++++----------- src/client/views/nodes/button/FontIconBox.tsx | 13 +- .../views/nodes/formattedText/DashFieldView.tsx | 150 ++------- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- src/client/views/nodes/formattedText/marks_rts.ts | 2 +- src/client/views/nodes/trails/PresBox.tsx | 18 +- src/client/views/pdf/PDFViewer.tsx | 3 +- src/fields/Doc.ts | 2 +- 32 files changed, 605 insertions(+), 532 deletions(-) (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx') diff --git a/package-lock.json b/package-lock.json index 196906718..111d1838a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5597,6 +5597,16 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "d3": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz", @@ -6977,6 +6987,28 @@ "is-symbol": "^1.0.2" } }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -6988,6 +7020,7 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { + "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -22660,6 +22693,12 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5a7894c08..bb12ce568 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1054,8 +1054,8 @@ export namespace Docs { return inst; } - export function WebanchorDocument(url?: string, options: DocumentOptions = {}, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.MARKER), url, options, id); + export function WebanchorDocument(options: DocumentOptions = {}, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.MARKER), undefined, options, id); } export function CollectionAnchorDocument(options: DocumentOptions = {}, id?: string) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 80a5f0993..fbfe306a4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -669,7 +669,10 @@ export class CurrentUserUtils { } static schemaTools():Button[] { - return [{ title: "Show preview", toolTip: "Show selection preview", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{ return toggleSchemaPreview(_readOnly_); }'}, }]; + return [ + {title: "Show preview", toolTip: "Show selection preview", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{ return toggleSchemaPreview(_readOnly_); }'} }, + {title: "Single Lines", toolTip: "Single Line Rows", btnType: ButtonType.ToggleButton, buttonText: "Single Line", icon: "eye", scripts:{ onClick: '{ return toggleSingleLineSchema(_readOnly_); }'} }, + ]; } static webTools() { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 3a192f712..695b003a6 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { action, observable, ObservableSet } from 'mobx'; +import { action, computed, observable, ObservableSet } from 'mobx'; import { AnimationSym, Doc, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { listSpec } from '../../fields/Schema'; @@ -10,6 +10,7 @@ import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; +import { KeyValueBox } from '../views/nodes/KeyValueBox'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; @@ -18,10 +19,13 @@ const { Howl } = require('howler'); export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) - @observable public DocumentViews = new Set(); + @observable _documentViews = new Set(); @observable public LinkAnchorBoxViews: DocumentView[] = []; @observable public RecordingEvent = 0; @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = []; + @computed public get DocumentViews() { + return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox)); + } private static _instance: DocumentManager; public static get Instance(): DocumentManager { @@ -78,7 +82,7 @@ export class DocumentManager { // this.LinkedDocumentViews.forEach(view => console.log(" LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " + // view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString)); } else { - this.DocumentViews.add(view); + this._documentViews.add(view); } this.callAddViewFuncs(view); }; @@ -96,7 +100,7 @@ export class DocumentManager { const index = this.LinkAnchorBoxViews.indexOf(view); this.LinkAnchorBoxViews.splice(index, 1); } else { - this.DocumentViews.delete(view); + this._documentViews.delete(view); } SelectionManager.DeselectView(view); }); @@ -104,13 +108,13 @@ export class DocumentManager { //gets all views public getDocumentViewsById(id: string) { const toReturn: DocumentView[] = []; - Array.from(DocumentManager.Instance.DocumentViews).map(view => { + DocumentManager.Instance.DocumentViews.forEach(view => { if (view.rootDoc[Id] === id) { toReturn.push(view); } }); if (toReturn.length === 0) { - Array.from(DocumentManager.Instance.DocumentViews).map(view => { + DocumentManager.Instance.DocumentViews.forEach(view => { const doc = view.rootDoc.proto; if (doc && doc[Id] && doc[Id] === id) { toReturn.push(view); @@ -133,7 +137,7 @@ export class DocumentManager { // ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href) // )?.rootDoc ?? toFind; - const docViewArray = Array.from(DocumentManager.Instance.DocumentViews); + const docViewArray = DocumentManager.Instance.DocumentViews; const passes = !doc ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined]; return passes.reduce( (pass, toReturn) => @@ -146,7 +150,7 @@ export class DocumentManager { public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { const views: DocumentView[] = []; - Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view)); + DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view)); return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined); }; public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { @@ -164,8 +168,8 @@ export class DocumentManager { toFindIn; const toReturn: DocumentView[] = []; - const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath)); - const lightViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => LightboxView.IsLightboxDocView(view.docViewPath)); + const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath)); + const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath)); // heuristic to return the "best" documents first: // choose a document in the lightbox first diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index f17a98616..70c2e3842 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -7,8 +7,6 @@ import * as typescriptlib from '!!raw-loader!./type_decls.d'; import * as ts from 'typescript'; import { Doc, Field } from '../../fields/Doc'; -import { ToScriptString } from '../../fields/FieldSymbols'; -import { ObjectField } from '../../fields/ObjectField'; import { RefField } from '../../fields/RefField'; import { ScriptField } from '../../fields/ScriptField'; import { scriptingGlobals, ScriptingGlobals } from './ScriptingGlobals'; @@ -60,7 +58,14 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an // let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript]; // let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)]; // let params: any[] = [Docs, ...fieldTypes]; - const compiledFunction = new Function(...paramNames, `return ${script}`); + const compiledFunction = (() => { + try { + return new Function(...paramNames, `return ${script}`); + } catch { + return undefined; + } + })(); + if (!compiledFunction) return { compiled: false, errors }; const { capturedVariables = {} } = options; const run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => { const argsArray: any[] = []; @@ -155,7 +160,7 @@ export type Traverser = (node: ts.Node, indentation: string) => boolean | void; export type TraverserParam = Traverser | { onEnter: Traverser; onLeave: Traverser }; export type Transformer = { transformer: ts.TransformerFactory; - getVars?: () => { capturedVariables: { [name: string]: Field } }; + getVars?: () => { [name: string]: Field }; }; export interface ScriptOptions { requiredType?: string; // does function required a typed return value @@ -205,16 +210,17 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp if (options.transformer) { const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true); const result = ts.transform(sourceFile, [options.transformer.transformer]); - if (options.transformer.getVars) { - const newCaptures = options.transformer.getVars(); + const newCaptures = options.transformer.getVars?.(); + if (Object.keys(newCaptures ?? {}).length) { // tslint:disable-next-line: prefer-object-spread - options.capturedVariables = Object.assign(capturedVariables, newCaptures.capturedVariables) as any; + //options.capturedVariables = Object.assign(capturedVariables, newCaptures!) as any; + + const transformed = result.transformed; + const printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed, + }); + script = printer.printFile(transformed[0]); } - const transformed = result.transformed; - const printer = ts.createPrinter({ - newLine: ts.NewLineKind.LineFeed, - }); - script = printer.printFile(transformed[0]); result.dispose(); } const paramNames: string[] = []; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 85d36dbf8..26b64bfdd 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -867,11 +867,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> -
- {hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} - {hideResizers || hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} +
+ {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} + {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} {hideTitle ? null : titleArea} - {hideOpenButton ?
: topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')} + {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
{hideResizers ? null : ( <> diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss index ed7ec9dc1..0955ba8ff 100644 --- a/src/client/views/EditableView.scss +++ b/src/client/views/EditableView.scss @@ -10,12 +10,14 @@ .editableView-container-editing-oneLine { width: 100%; + height: max-content; span { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: block; + p { + white-space: pre; + overflow: hidden; + text-overflow: ellipsis; + } } input { @@ -33,4 +35,3 @@ border: none; outline: none; } - \ No newline at end of file diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index d1311a60a..6b4132814 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -1,9 +1,11 @@ import React = require('react'); -import { action, observable } from 'mobx'; +import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as Autosuggest from 'react-autosuggest'; import { ObjectField } from '../../fields/ObjectField'; import './EditableView.scss'; +import { DocumentIconContainer } from './nodes/DocumentIcon'; +import { OverlayView } from './OverlayView'; export interface EditableProps { /** @@ -37,7 +39,8 @@ export interface EditableProps { onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void; autosuggestProps: Autosuggest.AutosuggestProps; }; - oneLine?: boolean; + oneLine?: boolean; // whether to display the editable view as a single input line or as a textarea + allowCRs?: boolean; // can carriage returns be entered editing?: boolean; isEditingCallback?: (isEditing: boolean) => void; menuCallback?: (x: number, y: number) => void; @@ -45,6 +48,7 @@ export interface EditableProps { showMenuOnLoad?: boolean; background?: string | undefined; placeholder?: string; + wrap?: string; // nowrap, pre-wrap, etc } /** @@ -55,7 +59,9 @@ export interface EditableProps { @observer export class EditableView extends React.Component { private _ref = React.createRef(); - private _inputref = React.createRef(); + private _inputref: HTMLInputElement | HTMLTextAreaElement | null = null; + _overlayDisposer?: () => void; + _editingDisposer?: IReactionDisposer; @observable _editing: boolean = false; constructor(props: EditableProps) { @@ -63,21 +69,53 @@ export class EditableView extends React.Component { this._editing = this.props.editing ? true : false; } + componentDidMount(): void { + this._editingDisposer = reaction( + () => this._editing, + editing => { + if (editing) { + setTimeout(() => { + if (this._inputref?.value.startsWith('=') || this._inputref?.value.startsWith(':=')) { + this._overlayDisposer?.(); + this._overlayDisposer = OverlayView.Instance.addElement(, { x: 0, y: 0 }); + } + }); + } else { + this._overlayDisposer?.(); + this._overlayDisposer = undefined; + } + }, + { fireImmediately: true } + ); + } + @action componentDidUpdate() { if (this._editing && this.props.editing === false) { - this._inputref.current?.value && this.finalizeEdit(this._inputref.current.value, false, true, false); + this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false); } else if (this.props.editing !== undefined) { this._editing = this.props.editing; } } componentWillUnmount() { - this._inputref.current?.value && this.finalizeEdit(this._inputref.current.value, false, true, false); + this._overlayDisposer?.(); + this._editingDisposer?.(); + this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false); } + onChange = (e: React.ChangeEvent) => { + const targVal = (e.target as any).value; + if (!(targVal.startsWith(':=') || targVal.startsWith('='))) { + this._overlayDisposer?.(); + this._overlayDisposer = undefined; + } else if (!this._overlayDisposer) { + this._overlayDisposer = OverlayView.Instance.addElement(, { x: 0, y: 0 }); + } + }; + @action - onKeyDown = (e: React.KeyboardEvent) => { + onKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Tab': e.stopPropagation(); @@ -89,13 +127,15 @@ export class EditableView extends React.Component { if (!e.currentTarget.value) this.props.OnEmpty?.(); break; case 'Enter': - e.stopPropagation(); - if (!e.ctrlKey) { - this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true); - } else if (this.props.OnFillDown) { - this.props.OnFillDown(e.currentTarget.value); - this._editing = false; - this.props.isEditingCallback?.(false); + if (this.props.allowCRs !== true) { + e.stopPropagation(); + if (!e.ctrlKey) { + this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true); + } else if (this.props.OnFillDown) { + this.props.OnFillDown(e.currentTarget.value); + this._editing = false; + this.props.isEditingCallback?.(false); + } } break; case 'Escape': @@ -103,9 +143,6 @@ export class EditableView extends React.Component { this._editing = false; this.props.isEditingCallback?.(false); break; - case ':': - this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y); - break; case 'ArrowUp': case 'ArrowDown': case 'ArrowLeft': @@ -117,6 +154,12 @@ export class EditableView extends React.Component { case 'Meta': case 'Control': break; + case ':': + if (this.props.menuCallback) { + this.props.menuCallback(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y); + break; + } + default: if (this.props.textCallback?.(e.key)) { this._editing = false; @@ -186,15 +229,32 @@ export class EditableView extends React.Component { onChange: this.props.autosuggestProps.onChange, }} /> - ) : ( + ) : this.props.oneLine !== false && this.props.GetValue()?.toString().indexOf('\n') === -1 ? ( (this._inputref = r)} style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minWidth: 20, background: this.props.background }} placeholder={this.props.placeholder} onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} defaultValue={this.props.GetValue()} autoFocus={true} + onChange={this.onChange} + onKeyDown={this.onKeyDown} + onKeyPress={this.stopPropagation} + onPointerDown={this.stopPropagation} + onClick={this.stopPropagation} + onPointerUp={this.stopPropagation} + /> + ) : ( +