Procházet zdrojové kódy

something before edit and save

Ivan Asmer před 4 roky
rodič
revize
28148d4cc8
3 změnil soubory, kde provedl 151 přidání a 48 odebrání
  1. 12 4
      src/App.css
  2. 135 44
      src/App.js
  3. 4 0
      src/front-models.js

+ 12 - 4
src/App.css

@@ -64,11 +64,12 @@
 .Cell {
     width: 100%;
     max-width: 100%;
-    overflow: hidden;
+    overflow: auto;
     border: 1px solid #BBB;
     padding: 10px;
     text-align: center;
-    font-size: 0.8em;
+    font-size: 0.7em;
+    
 }
 
 .Th {
@@ -80,8 +81,8 @@
 }
 
 .Row {
-    max-height: 50px;
-    height: 50px;
+    max-height: 150px;
+    height: 150px;
 }
 
 textarea {
@@ -117,4 +118,11 @@ input.Search {
     width: 100%;
 }
 
+.ObjectShortView {
+    border: 1px solid #777;
+    border-radius: 5px;
+    background-color: #DDD;
+    font-size: 1.2em;
+    overflow: hidden
+}
 

+ 135 - 44
src/App.js

@@ -24,15 +24,38 @@ const Th = p =>
 <div className='Th' {...p} />
 
 //<div className='Row' style={{position: 'relative',top}}>
-const Row = ({top, children}) =>
-<div className='Row' >
-    {children}
-</div>
+const Row = ({top, selected, children}) => { 
+    return (
+        <>
+            <div className={`Row ${selected ? 'selected' : ''}` } >
+                {children}
+            </div>
+        </>
+    )
+}
 
-const Cell = ({children, ...props}) =>
-<div className='Cell' {...props}>
-    {children && children.toString()}
-</div>
+const Cell = ({children, options, record, field, selected, ...props}) => {
+    let Formatter = React.Fragment
+
+    const viewOrEdit = (!record.constructor.inputs || record.constructor.inputs.some(input => input.name === field.name)) && selected ? 'edit' : 'view'
+    if (field.type.name in options[viewOrEdit].formatters){
+        Formatter = options[viewOrEdit].formatters[field.type.name]
+    }
+    if (field.name in options[viewOrEdit].fields){
+        Formatter = options[viewOrEdit].fields[field.name]
+    }
+    if (children && typeof children === 'object'){ 
+        if (children.constructor.name in options[viewOrEdit].formatters)
+            Formatter = options[viewOrEdit].formatters[children.constructor.name]
+        else
+            Formatter = options[viewOrEdit].formatters.Object
+    }
+    return( 
+        <div className='Cell' {...props}>
+            <Formatter {...props} field={field}>{Formatter === React.Fragment ? (children && children.toString()) : children}</Formatter>
+        </div>
+    )
+}
 
 
 const ModelListItem = "div"
@@ -79,38 +102,43 @@ const GridHeader = ({fields, sort, onSort}) =>
     {fields.map(field => <GridHeaderItem field={field} sort={sort} onClick={() => onSort(field.name)}/>)}
 </div>
 
-const VirtualScroll = ({gridHeight, count, rowHeight, row=Row, onScroll, records, skip}) => { 
+const VirtualScroll = ({options, gridHeight, count, rowHeight, components:Components={Row, Cell}, onScroll, records, skip}) => { 
     //const [records, setRecords] = useState([])
     const limit = gridHeight/rowHeight
-    const Row = row
+    const {Row, Cell} = Components
 
+    const [edit, setEdit] = useState({field: null, record: null})
 
     const timeout = useRef(0)
 
     return (
     <div className='GridViewport' 
          onScroll={e => {
-             let skip = Math.ceil(e.target.scrollTop/rowHeight)
-             if (skip < 0) skip = 0
-             if (skip > count - limit) skip = count - limit
-
-             clearInterval(timeout.current)
-             timeout.current = setTimeout(() => {
-                 console.log(skip, limit)
-                 onScroll(skip,limit)
-             }, 1000)
+             //let skip = Math.ceil(e.target.scrollTop/rowHeight)
+             //if (skip < 0) skip = 0
+             //if (skip > count - limit) skip = count - limit
+
+             //clearInterval(timeout.current)
+             //timeout.current = setTimeout(() => {
+                 //console.log(skip, limit)
+                 //onScroll(skip,limit)
+             //}, 1000)
          }} 
          style={{maxHeight: gridHeight, height: gridHeight}} >
         <div className='GridContent' 
              style={{height: count*rowHeight, minHeight: count*rowHeight, maxHeight: count*rowHeight}} >
         { /*      <div style={{height: skip*rowHeight}} /> */ }
-            {records && records.map((record,i) => 
-                                            <Row key={i}>
+            {records && records.map((record,i) =><React.Fragment key={i}> 
+                                            <Row options={options} selected={edit.record === record}>
                                                 {record.constructor.fields.map(field => 
-                                                    <Cell>
-                                                        {record[field.name] === 'object' ? record[field.name].toString() :  record[field.name]}
+                                                    <Cell record={record} key={field.name} field={field} options={options} onClick={() => setEdit({record, field})}
+                                                            selected={edit.record === record} >
+                                                        {record[field.name]}
                                                     </Cell>)}
-                                            </Row>)}
+                                            </Row>
+                {edit.record === record && <Row selected>additional inputs and save/cancel</Row>}
+                </React.Fragment>
+            )}
         {/*            <div style={{height: (count - skip - records.length)*rowHeight}} /> */ }
         </div>
     </div>
@@ -120,7 +148,7 @@ const VirtualScroll = ({gridHeight, count, rowHeight, row=Row, onScroll, records
 
 
 
-const ModelView = ({model, components:Components={Search, Count, GridHeader, Grid:VirtualScroll}, rowHeight=50, gridHeight=500, overload=2}) => {
+const ModelView = ({model, options, components:Components={Search, Count, GridHeader, Grid:VirtualScroll}, rowHeight=150, gridHeight=500, overload=2}) => {
 
 
 
@@ -141,25 +169,28 @@ const ModelView = ({model, components:Components={Search, Count, GridHeader, Gri
         //setCursorCalls({sort:cursorCalls.sort, skip: skip ? [skip] : undefined, limit: overloadedRowCount ? [overloadedRowCount] : undefined})
     //}, [scroll])
 
-    const timeout = useRef(0)
-    useEffect(() => {
-        clearInterval(timeout.current)
-        timeout.current = setTimeout(() => {
-            setQuery(searchQueryBuilder(search, model))
-        }, 1000)
-    },[search, model])
 
     useEffect(() => {
         model.count(query, cursorCalls).then(count => setCount(count))
         model.find(query, cursorCalls).then(records => Promise.all(records)).then(records => setRecords(records))
     }, [query, model, cursorCalls])
 
-    //console.log(records)
+    const timeout = useRef(0)
+    useEffect(() => {
+        clearInterval(timeout.current)
+        if (!search) 
+            setQuery(searchQueryBuilder(search, model))
+        else {
+            timeout.current = setTimeout(() => {
+                setQuery(searchQueryBuilder(search, model))
+            },1000)
+        }
+    },[search, model])
 
     return (
         <>
-                <Components.Search value={search} onChange={({target: {value}}) => setSearch(value)}/>
-                <Components.Count> 
+                <Components.Search options={options} value={search} onChange={({target: {value}}) => setSearch(value)}/>
+                <Components.Count options={options}>   
                     {count}
                 </Components.Count> 
                 <Components.GridHeader fields={model.fields} 
@@ -169,6 +200,7 @@ const ModelView = ({model, components:Components={Search, Count, GridHeader, Gri
                             })}/>
 
                 {records && <Components.Grid 
+                                options={options}
                                 skip={skip}
                                 count={count}
                                 records={records}
@@ -183,16 +215,80 @@ const ModelView = ({model, components:Components={Search, Count, GridHeader, Gri
     )
 }
 
-const Admin = ({models, components:Components={ModelList, Search}}) => {
+const ObjectShortView = ({children}) => {
+    const [record, setRecord] = useState(children)
+    if ('then' in children){
+        console.log('load')
+        children.then(child => setRecord({...child}))
+    }
+    if (children._id){
+        return (
+        <div className="ObjectShortView">
+            {record.name || record.key || record._id}
+        </div>
+        )
+    }
+    else {
+        return (
+            <pre>
+            {JSON.stringify(record, null, 4)}
+            </pre>
+        )
+    }
+}
+
+
+const defaultAdminOptions = 
+    {
+        view: {
+            formatters:{
+                ID: ({children}) => <b>{children && children.slice(-6).toUpperCase()}</b>,
+                String: ({children}) => <>{children && children.length > 100 ? children.slice(0,100) + '...' : children}</>,
+                Object: ObjectShortView,
+                Array: ({children}) => <>{children.map(child => <ObjectShortView children={child} />)}</>
+            },
+            fields:{
+                createdAt: ({children}) => <>{new Date(+children).toISOString()}</> ,
+                url: ({children}) => <a href={children}>{children}</a> 
+            },
+            models: {
+
+            }
+        },
+
+        edit: {
+            formatters:{
+                ID: ({children}) => <b>{children && children.slice(-6).toUpperCase()}</b>,
+                String: ({children, ...props}) => <textarea value={children} {...props}/>,
+                Int: ({children, ...props}) => <input type='number' value={children} {...props}/>,
+                Float: ({children, ...props}) => <input type='number' value={children} {...props}/>,
+                Object: ObjectShortView,
+                Array: ({children}) => <>{children.map(child => <ObjectShortView children={child} />)}</>
+            },
+            fields:{
+                createdAt: ({children}) => <input value={new Date(+children).toISOString()} />,
+                url: ({children}) => <a href={children}>{children}</a> 
+            },
+            models: {
+
+            }
+        }
+    }
+
+
+const Admin = ({models, components:{ModelList:ML=ModelList, Search:S=Search, ModelListItem:MLI=ModelListItem, ModelView:MV=ModelView, Count:C=Count, GridHeader:GH=GridHeader, Grid:G=VirtualScroll}={},
+                options=defaultAdminOptions
+                }) => {
     const [selected, setSelected] = useState()
 
+    const mergedOptions = {...defaultAdminOptions, ...options}
+
 
     return (
         <>
-            <Components.ModelList models={models} onChange={(name) => setSelected(name)} selected={selected}/>
+            <ML Item={MLI} models={models} onChange={(name) => setSelected(name)} selected={selected}/>
             <content>
-
-                {selected && <ModelView model={models[selected]} /> }
+                {selected && <MV options={mergedOptions} model={models[selected]} components={{Search: S, Count: C, GridHeader:GH, Grid:G}} /> }
             </content>
         </>
     )
@@ -201,11 +297,8 @@ const Admin = ({models, components:Components={ModelList, Search}}) => {
 
 function App() {
     let [models, setModels] = useState()
-    let [queryText,  setQueryText]  = useState('{manufacturerName: "TOYOTA"}')
-    let [query,  setQuery]  = useState({})
     models || createModels2(gql).then(models => setModels(models))
 
-    console.log(query)
     const classes = models
     console.log(classes)
     //if (classes && Object.keys(classes).length){
@@ -218,8 +311,6 @@ function App() {
             {models && <Admin models={models} />}
             {/*models && <ModelGrid model={models.Image} rowHeight={50} gridHeight={700} overload={5} query={query} 
                         cellDoubleClick={(name, text, _id) => setQueryText(`{${name}:\`${text}\`}`)}/> */}
-            <textarea onChange={e => setQueryText(e.target.value)} value={queryText} />
-            <button onClick={() => setQuery(eval(`(${queryText})`))}>Run</button>
         </div>
     );
 }

+ 4 - 0
src/front-models.js

@@ -179,6 +179,10 @@ export default async function createModels2(gql, config={create: 'Upsert', updat
                     return this.type.fields
                 }
 
+                static get inputs(){
+                    return this.input.inputFields
+                }
+
                 async save(){
                     if (this.empty) throw new ReferenceError('Cannot save empty object')
                     const data = {}