mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-10-28 16:00:24 +01:00
fixed relation and file custom change event trigger
This commit is contained in:
1 parent
5cc95a2e63
commit
6012ba701d
5 files changed
+204
-203
No files matched your search
@@ -47,8 +47,8 @@ The field cannot be presentable if hidden.`),n})})))},()=>{if(i.showHidden)retur
|
||||
This field is disabled if "Only domains" is set.`)})),t.input({type:`text`,id:n+`.exceptDomains`,disabled:()=>!app.utils.isEmpty(e.field.onlyDomains),name:()=>`fields.${e.fieldIndex}.exceptDomains`,value:()=>app.utils.joinNonEmpty(e.field.exceptDomains),onchange:n=>e.field.exceptDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.onlyDomains`},t.span({className:`txt`},`Only domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are ONLY allowed.
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function J(e){return t.div({className:`record-field-view field-type-email`},()=>{let n=e.record[e.field.name]||``;return n?e.short?t.span({className:`txt txt-ellipsis`,textContent:app.utils.truncate(n)}):n:t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.email={icon:`ri-mail-line`,label:`Email`,settings:ln,input:cn,view:J,filterModifiers:e=>[`lower`],dummyData:(e,n=!1)=>`test_${app.utils.randomString(3,`123567890`)}@example.com`};function un(e){let n=`url_`+app.utils.randomString();return t.div({className:`record-field-input field-type-url`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.url.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),t.input({type:`url`,id:n,spellcheck:!1,name:()=>e.field.name,required:()=>e.field.required,value:()=>e.record[e.field.name]||``,oninput:n=>e.record[e.field.name]=n.target.value})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function Y(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.exceptDomains`},t.span({className:`txt`},`Except domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are NOT allowed.
|
||||
This field is disabled if "Only domains" is set.`)})),t.input({type:`text`,id:n+`.exceptDomains`,disabled:()=>!app.utils.isEmpty(e.field.onlyDomains),name:()=>`fields.${e.fieldIndex}.exceptDomains`,value:()=>app.utils.joinNonEmpty(e.field.exceptDomains),onchange:n=>e.field.exceptDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.onlyDomains`},t.span({className:`txt`},`Only domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are ONLY allowed.
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function dn(e){return t.div({className:`record-field-view field-type-url`},()=>{let n=e.record[e.field.name]||``;return n?t.a({href:()=>n,className:`txt txt-ellipsis`,rel:`noopener noreferrer`,target:`_blank`,textContent:app.utils.truncate(n),ariaDescription:app.attrs.tooltip(`Open in new tab`),onclick:e=>{e.stopPropagation()}}):t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.url={icon:`ri-link`,label:`URL`,settings:Y,input:un,view:dn,dummyData:(e,n=!1)=>`https://example.com`};function fn(e){let n=`date_`+app.utils.randomString();return t.div({className:`record-field-input field-type-date`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.date.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),t.input({id:n,step:1,type:`datetime-local`,name:()=>e.field.name,required:()=>e.field.required,value:()=>app.utils.toDatetimeLocalInputValue(e.record[e.field.name]),onchange:n=>{e.record[e.field.name]=app.utils.toRFC3339Datetime(n.target.value)}})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function pn(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.min`},t.span({className:`txt`},`Min date (Local)`)),t.input({type:`datetime-local`,id:n+`.min`,step:1,name:()=>`fields.${e.fieldIndex}.min`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.min),onchange:n=>{e.field.min=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.max`},t.span({className:`txt`},`Max date (Local)`)),t.input({type:`datetime-local`,id:n+`.max`,step:1,name:()=>`fields.${e.fieldIndex}.max`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.max),onchange:n=>{e.field.max=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function mn(e){return t.div({className:`record-field-view field-type-date`},app.components.formattedDate({value:()=>e.record[e.field.name],short:()=>e.short}))}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.date={icon:`ri-calendar-line`,label:`Datetime`,settings:pn,input:fn,view:mn,dummyData:(e,n=!1)=>new Date().toISOString().replaceAll(`T`,` `)};function hn(e){delete e.clone[e.field.name]}function gn(e){let n=[{label:`Create`,value:1},{label:`Update`,value:2},{label:`Create/Update`,value:3}];function r(e){return e.onCreate&&e.onUpdate?3:e.onUpdate?2:1}function i(n){switch(n){case 1:e.field.onCreate=!0,e.field.onUpdate=!1;break;case 2:e.field.onCreate=!1,e.field.onUpdate=!0;break;case 3:e.field.onCreate=!0,e.field.onUpdate=!0;break}}let a=store({isDropdownOpen:!1});return app.components.fieldSettings(e,{header:t.div({className:`field header-select autodate-select`,ariaDescription:app.attrs.tooltip(`Auto set on`,`left`),onmount:()=>{i(r(e.field))}},app.components.select({required:!0,options:n,disabled:()=>e.originalCollection?.system,value:()=>r(e.field),onchange:e=>i(e?.[0]?.value),ondropdowntoggle:e=>{a.isDropdownOpen=e.newState==`open`}}))})}func
Line truncated
|
||||
Leave empty for no restriction.`)})),app.components.select({max:99,placeholder:`No restriction`,options:app.utils.mimeTypes.map(e=>({value:e.mimeType,label:()=>t.div({className:`inline-flex gap-10`},t.span({className:`txt`},e.ext||`-`),t.small({className:`txt-hint`},e.mimeType))})),name:()=>`fields.${e.fieldIndex}.mimeTypes`,value:()=>app.utils.toArray(e.field.mimeTypes),onchange:n=>e.field.mimeTypes=n.map(e=>e.value)})),t.div({className:`field-help`},t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`mimeTypesDropdown`},t.span({className:`txt`},`Choose presets`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`mimeTypesDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`image/jpeg`,`image/png`,`image/svg+xml`,`image/gif`,`image/webp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Images (jpg, png, svg, gif, webp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Documents (pdf, doc/docx, xls/xlsx)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`video/mp4`,`video/mpeg`,`video/x-msvideo`,`video/quicktime`,`video/3gpp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Videos (mp4, mpeg, avi, mov, 3gp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/zip`,`application/x-7z-compressed`,`application/x-rar-compressed`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Archives (zip, 7zip, rar)`})))),t.div({className:()=>e.field.maxSelect>1?`col-sm-6`:`col-sm-9`},t.div({className:`field`},t.label({htmlFor:n+`.thumbs`},t.span({className:`txt`},`Thumb sizes`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of additional thumb sizes for image files, along with the default thumb size of 100x100. The thumbs are generated lazily on first access.`)})),t.input({type:`text`,id:n+`.thumbs`,placeholder:`e.g. 50x50, 480x720`,name:()=>`fields.${e.fieldIndex}.thumbs`,value:()=>app.utils.joinNonEmpty(e.field.thumbs),onchange:n=>e.field.thumbs=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},t.span({className:`txt m-r-5`},`Use comma as separator.`),t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`thumbFormatsDropdown`},t.span({className:`txt`},`Supported formats`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`thumbFormatsDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.ul({className:`m-0 p-l-sm`},t.li(null,t.strong(null,`WxH`),t.span(null,` (e.g. 100x50) - crop to WxH viewbox (from center)`)),t.li(null,t.strong(null,`WxHt`),t.span(null,` (e.g. 100x50t) - crop to WxH viewbox (from top)`)),t.li(null,t.strong(null,`WxHb`),t.span(null,` (e.g. 100x50b) - crop to WxH viewbox (from bottom)`)),t.li(null,t.strong(null,`WxHf`),t.span(null,` (e.g. 100x50f) - fit inside a WxH viewbox (without cropping)`)),t.li(null,t.strong(null,`0xH`),t.span(null,` (e.g. 0x50) - resize to H height preserving the aspect ratio`)),t.li(null,t.strong(null,`Wx0`),t.span(null,` (e.g. 100x0) - resize to W width preserving the aspect ratio`)))))),t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},`Max size`),t.input({type:`number`,id:n+`.maxSize`,step:1,min:0,max:2**53-1,placeholder:`~5MB default`,name:()=>`fields.${e.fieldIndex}.maxSize`,value:()=>e.field.maxSize||``,oninput:n=>e.field.maxSize=parseInt(n.target.value,10)})),t.div({className:`field-help`},`In bytes.`)),()=>{if(e.field.maxSelect>1)return t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSelect`},`Max select`),t.input({type:`number`,id:n+`.maxSelect`,placeholder:`Default to single`,step:1,min:2,required:!0,max:2**53-1,name:()=>`fields.${e.fieldIndex}.maxSelect`,value:()=>e.field.maxSelect||``,onchange:e=>{let n=parseInt(e.target.value,10);n>1?props.field.maxSelect=n:props.field.maxSelect=1}})))},t.div({className:`col-sm-12`},t.div({className:`field m-t-5 m-b-5`},t.input({className:`switch`,type:`checkbox`,id:n+`.protected`,name:()=>`fields.${e.fieldIndex}.protected`,checked:()=>!!e.field.protected,onchange:n=>e.field.protected=n.target.checked}),t.label({htmlFor:n+`.protected`},t.span({className:`txt`},`Protected`),t.small({className:`txt-hint`},`Files will require View API rule permissions and file token (`,t.a({href:`https://pocketbase.io/docs/files-handling#protected-files`,target:`_blank`,rel:`noopener noreferrer`,textContent:`Learn more`}),`).`)))),t.div({className:`col-sm-12`},t.di
Line truncated
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function dn(e){return t.div({className:`record-field-view field-type-url`},()=>{let n=e.record[e.field.name]||``;return n?t.a({href:()=>n,className:`txt txt-ellipsis`,rel:`noopener noreferrer`,target:`_blank`,textContent:app.utils.truncate(n),ariaDescription:app.attrs.tooltip(`Open in new tab`),onclick:e=>{e.stopPropagation()}}):t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.url={icon:`ri-link`,label:`URL`,settings:Y,input:un,view:dn,dummyData:(e,n=!1)=>`https://example.com`};function fn(e){let n=`date_`+app.utils.randomString();return t.div({className:`record-field-input field-type-date`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.date.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),t.input({id:n,step:1,type:`datetime-local`,name:()=>e.field.name,required:()=>e.field.required,value:()=>app.utils.toDatetimeLocalInputValue(e.record[e.field.name]),onchange:n=>{e.record[e.field.name]=app.utils.toRFC3339Datetime(n.target.value)}})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function pn(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.min`},t.span({className:`txt`},`Min date (Local)`)),t.input({type:`datetime-local`,id:n+`.min`,step:1,name:()=>`fields.${e.fieldIndex}.min`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.min),onchange:n=>{e.field.min=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.max`},t.span({className:`txt`},`Max date (Local)`)),t.input({type:`datetime-local`,id:n+`.max`,step:1,name:()=>`fields.${e.fieldIndex}.max`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.max),onchange:n=>{e.field.max=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function mn(e){return t.div({className:`record-field-view field-type-date`},app.components.formattedDate({value:()=>e.record[e.field.name],short:()=>e.short}))}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.date={icon:`ri-calendar-line`,label:`Datetime`,settings:pn,input:fn,view:mn,dummyData:(e,n=!1)=>new Date().toISOString().replaceAll(`T`,` `)};function hn(e){delete e.clone[e.field.name]}function gn(e){let n=[{label:`Create`,value:1},{label:`Update`,value:2},{label:`Create/Update`,value:3}];function r(e){return e.onCreate&&e.onUpdate?3:e.onUpdate?2:1}function i(n){switch(n){case 1:e.field.onCreate=!0,e.field.onUpdate=!1;break;case 2:e.field.onCreate=!1,e.field.onUpdate=!0;break;case 3:e.field.onCreate=!0,e.field.onUpdate=!0;break}}let a=store({isDropdownOpen:!1});return app.components.fieldSettings(e,{header:t.div({className:`field header-select autodate-select`,ariaDescription:app.attrs.tooltip(`Auto set on`,`left`),onmount:()=>{i(r(e.field))}},app.components.select({required:!0,options:n,disabled:()=>e.originalCollection?.system,value:()=>r(e.field),onchange:e=>i(e?.[0]?.value),ondropdowntoggle:e=>{a.isDropdownOpen=e.newState==`open`}}))})}func
Line truncated
|
||||
Leave empty for no restriction.`)})),app.components.select({max:99,placeholder:`No restriction`,options:app.utils.mimeTypes.map(e=>({value:e.mimeType,label:()=>t.div({className:`inline-flex gap-10`},t.span({className:`txt`},e.ext||`-`),t.small({className:`txt-hint`},e.mimeType))})),name:()=>`fields.${e.fieldIndex}.mimeTypes`,value:()=>app.utils.toArray(e.field.mimeTypes),onchange:n=>e.field.mimeTypes=n.map(e=>e.value)})),t.div({className:`field-help`},t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`mimeTypesDropdown`},t.span({className:`txt`},`Choose presets`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`mimeTypesDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`image/jpeg`,`image/png`,`image/svg+xml`,`image/gif`,`image/webp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Images (jpg, png, svg, gif, webp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Documents (pdf, doc/docx, xls/xlsx)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`video/mp4`,`video/mpeg`,`video/x-msvideo`,`video/quicktime`,`video/3gpp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Videos (mp4, mpeg, avi, mov, 3gp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/zip`,`application/x-7z-compressed`,`application/x-rar-compressed`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Archives (zip, 7zip, rar)`})))),t.div({className:()=>e.field.maxSelect>1?`col-sm-6`:`col-sm-9`},t.div({className:`field`},t.label({htmlFor:n+`.thumbs`},t.span({className:`txt`},`Thumb sizes`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of additional thumb sizes for image files, along with the default thumb size of 100x100. The thumbs are generated lazily on first access.`)})),t.input({type:`text`,id:n+`.thumbs`,placeholder:`e.g. 50x50, 480x720`,name:()=>`fields.${e.fieldIndex}.thumbs`,value:()=>app.utils.joinNonEmpty(e.field.thumbs),onchange:n=>e.field.thumbs=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},t.span({className:`txt m-r-5`},`Use comma as separator.`),t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`thumbFormatsDropdown`},t.span({className:`txt`},`Supported formats`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`thumbFormatsDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.ul({className:`m-0 p-l-sm`},t.li(null,t.strong(null,`WxH`),t.span(null,` (e.g. 100x50) - crop to WxH viewbox (from center)`)),t.li(null,t.strong(null,`WxHt`),t.span(null,` (e.g. 100x50t) - crop to WxH viewbox (from top)`)),t.li(null,t.strong(null,`WxHb`),t.span(null,` (e.g. 100x50b) - crop to WxH viewbox (from bottom)`)),t.li(null,t.strong(null,`WxHf`),t.span(null,` (e.g. 100x50f) - fit inside a WxH viewbox (without cropping)`)),t.li(null,t.strong(null,`0xH`),t.span(null,` (e.g. 0x50) - resize to H height preserving the aspect ratio`)),t.li(null,t.strong(null,`Wx0`),t.span(null,` (e.g. 100x0) - resize to W width preserving the aspect ratio`)))))),t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},`Max size`),t.input({type:`number`,id:n+`.maxSize`,step:1,min:0,max:2**53-1,placeholder:`~5MB default`,name:()=>`fields.${e.fieldIndex}.maxSize`,value:()=>e.field.maxSize||``,oninput:n=>e.field.maxSize=parseInt(n.target.value,10)})),t.div({className:`field-help`},`In bytes.`)),()=>{if(e.field.maxSelect>1)return t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSelect`},`Max select`),t.input({type:`number`,id:n+`.maxSelect`,placeholder:`Default to single`,step:1,min:2,required:!0,max:2**53-1,name:()=>`fields.${e.fieldIndex}.maxSelect`,value:()=>e.field.maxSelect||``,onchange:e=>{let n=parseInt(e.target.value,10);n>1?props.field.maxSelect=n:props.field.maxSelect=1}})))},t.div({className:`col-sm-12`},t.div({className:`field m-t-5 m-b-5`},t.input({className:`switch`,type:`checkbox`,id:n+`.protected`,name:()=>`fields.${e.fieldIndex}.protected`,checked:()=>!!e.field.protected,onchange:n=>e.field.protected=n.target.checked}),t.label({htmlFor:n+`.protected`},t.span({className:`txt`},`Protected`),t.small({className:`txt-hint`},`Files will require View API rule permissions and file token (`,t.a({href:`https://pocketbase.io/docs/files-handling#protected-files`,target:`_blank`,rel:`noopener noreferrer`,textContent:`Learn more`}),`).`)))),t.div({className:`col-sm-12`},t.di
Line truncated
|
||||
`),oninput:n=>{let r=n.target.value.trimStart().replaceAll(`
|
||||
|
||||
`,`
|
||||
Vendored
+1
-1
@@ -13,7 +13,7 @@
|
||||
|
||||
<!-- prism -->
|
||||
<script src="./libs/prism/prism.js" data-manual></script>
|
||||
<script type="module" crossorigin src="./assets/index-DhtJzO0I.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-OxsdchXY.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-BLIFQr7L.css">
|
||||
</head>
|
||||
|
||||
+1
-2
@@ -4,8 +4,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"format": "dprint fmt"
|
||||
"build": "dprint fmt && vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
"leaflet": "^1.9.4",
|
||||
|
||||
+121
-119
@@ -57,7 +57,7 @@ export function input(props) {
|
||||
|
||||
// trigger custom change event for clearing field errors
|
||||
function triggerChangeEvent() {
|
||||
fieldEl?.dispatchEvent(
|
||||
fieldContentEl?.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { data: props },
|
||||
bubbles: true,
|
||||
@@ -92,7 +92,6 @@ export function input(props) {
|
||||
const fileInput = t.input({
|
||||
type: "file",
|
||||
hidden: true,
|
||||
name: () => props.field.name,
|
||||
multiple: () => props.field.maxSelect > 1,
|
||||
accept: () => props.field.mimeTypes?.join(",") || undefined,
|
||||
onchange: (e) => {
|
||||
@@ -101,7 +100,125 @@ export function input(props) {
|
||||
},
|
||||
});
|
||||
|
||||
const fieldEl = t.div(
|
||||
const fieldContentEl = t.output(
|
||||
{
|
||||
className: "field-content",
|
||||
name: () => props.field.name,
|
||||
},
|
||||
// @todo enable ordering new files before/inbetween existing
|
||||
app.components.sortable({
|
||||
className: "list",
|
||||
data: () => {
|
||||
const vals = app.utils.toArray(props.record[props.field.name]);
|
||||
|
||||
let hadInvalid = false;
|
||||
// filter empty or invalid values (e.g. from old serialized draft)
|
||||
for (let i = vals.length - 1; i >= 0; i--) {
|
||||
if (typeof vals[i] == "string" || vals[i] instanceof Blob) {
|
||||
continue; // valid
|
||||
}
|
||||
|
||||
hadInvalid = true;
|
||||
vals.splice(i, 1);
|
||||
}
|
||||
|
||||
// update record model to prevent conflict with required and other validators
|
||||
if (hadInvalid) {
|
||||
props.record[props.field.name] = vals;
|
||||
}
|
||||
|
||||
return vals;
|
||||
},
|
||||
onchange: (sortedList) => {
|
||||
props.record[props.field.name] = sortedList;
|
||||
triggerChangeEvent();
|
||||
},
|
||||
dataItem: (nameOrFile, i) => {
|
||||
return t.div(
|
||||
{
|
||||
rid: nameOrFile,
|
||||
className: () => `list-item highlight ${isDeleted(nameOrFile) ? "deleted" : ""}`,
|
||||
},
|
||||
t.div({ className: "content gap-10" }, () => {
|
||||
if (typeof nameOrFile == "string") {
|
||||
return [
|
||||
app.components.recordFileThumb({
|
||||
record: props.record,
|
||||
filename: nameOrFile,
|
||||
}),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
ariaDescription: app.attrs.tooltip("Open in new tab"),
|
||||
onclick: async () => {
|
||||
const token = await app.getFileToken(props.record.collectionId);
|
||||
const url = app.pb.files.getURL(props.record, nameOrFile, {
|
||||
token,
|
||||
});
|
||||
window.open(url, "_blank", "noreferrer,noopener");
|
||||
},
|
||||
},
|
||||
t.span({ className: "txt link-primary" }, nameOrFile),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
app.components.uploadedFileThumb({
|
||||
file: nameOrFile,
|
||||
}),
|
||||
t.span({ className: "label success" }, "New"),
|
||||
t.span({ className: "txt" }, nameOrFile.name),
|
||||
];
|
||||
}),
|
||||
t.div(
|
||||
{ className: "actions" },
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm secondary transparent circle",
|
||||
ariaLabel: app.attrs.tooltip("Remove file"),
|
||||
hidden: () => isDeleted(nameOrFile),
|
||||
onclick: () => toDelete(nameOrFile),
|
||||
},
|
||||
t.i({ className: "ri-close-line", ariaHidden: true }),
|
||||
),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm warning transparent",
|
||||
hidden: () => !isDeleted(nameOrFile),
|
||||
onclick: () => restoreDeleted(nameOrFile),
|
||||
},
|
||||
t.span({ className: "txt" }, "Restore"),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
t.hr({
|
||||
className: "m-t-5 m-b-0",
|
||||
hidden: () => app.utils.toArray(props.record[props.field.name]).length > 0,
|
||||
}),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm secondary block",
|
||||
title: () => local.maxReached ? "Max allowed files reached" : undefined,
|
||||
disabled: () => local.maxReached,
|
||||
onclick: (e) => {
|
||||
if (!local.maxReached) {
|
||||
fileInput?.click();
|
||||
}
|
||||
document.activeElement?.blur();
|
||||
},
|
||||
},
|
||||
t.i({ className: "ri-upload-cloud-line", ariaHidden: true }),
|
||||
t.span({ className: "txt" }, "Upload or drop new file"),
|
||||
),
|
||||
);
|
||||
|
||||
return t.div(
|
||||
{
|
||||
className: "record-field-input field-type-file",
|
||||
ondragover: (e) => {
|
||||
@@ -130,120 +247,7 @@ export function input(props) {
|
||||
t.span({ className: "txt" }, () => props.field.name),
|
||||
),
|
||||
fileInput,
|
||||
t.div(
|
||||
{ className: "field-content" },
|
||||
// @todo enable ordering new files before/inbetween existing
|
||||
app.components.sortable({
|
||||
className: "list",
|
||||
data: () => {
|
||||
const vals = app.utils.toArray(props.record[props.field.name]);
|
||||
|
||||
let hadInvalid = false;
|
||||
// filter empty or invalid values (e.g. from old serialized draft)
|
||||
for (let i = vals.length - 1; i >= 0; i--) {
|
||||
if (typeof vals[i] == "string" || vals[i] instanceof Blob) {
|
||||
continue; // valid
|
||||
}
|
||||
|
||||
hadInvalid = true;
|
||||
vals.splice(i, 1);
|
||||
}
|
||||
|
||||
// update record model to prevent conflict with required and other validators
|
||||
if (hadInvalid) {
|
||||
props.record[props.field.name] = vals;
|
||||
}
|
||||
|
||||
return vals;
|
||||
},
|
||||
onchange: (sortedList) => {
|
||||
props.record[props.field.name] = sortedList;
|
||||
triggerChangeEvent();
|
||||
},
|
||||
dataItem: (nameOrFile, i) => {
|
||||
return t.div(
|
||||
{
|
||||
rid: nameOrFile,
|
||||
className: () => `list-item highlight ${isDeleted(nameOrFile) ? "deleted" : ""}`,
|
||||
},
|
||||
t.div({ className: "content gap-10" }, () => {
|
||||
if (typeof nameOrFile == "string") {
|
||||
return [
|
||||
app.components.recordFileThumb({
|
||||
record: props.record,
|
||||
filename: nameOrFile,
|
||||
}),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
ariaDescription: app.attrs.tooltip("Open in new tab"),
|
||||
onclick: async () => {
|
||||
const token = await app.getFileToken(props.record.collectionId);
|
||||
const url = app.pb.files.getURL(props.record, nameOrFile, {
|
||||
token,
|
||||
});
|
||||
window.open(url, "_blank", "noreferrer,noopener");
|
||||
},
|
||||
},
|
||||
t.span({ className: "txt link-primary" }, nameOrFile),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
app.components.uploadedFileThumb({
|
||||
file: nameOrFile,
|
||||
}),
|
||||
t.span({ className: "label success" }, "New"),
|
||||
t.span({ className: "txt" }, nameOrFile.name),
|
||||
];
|
||||
}),
|
||||
t.div(
|
||||
{ className: "actions" },
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm secondary transparent circle",
|
||||
ariaLabel: app.attrs.tooltip("Remove file"),
|
||||
hidden: () => isDeleted(nameOrFile),
|
||||
onclick: () => toDelete(nameOrFile),
|
||||
},
|
||||
t.i({ className: "ri-close-line", ariaHidden: true }),
|
||||
),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm warning transparent",
|
||||
hidden: () => !isDeleted(nameOrFile),
|
||||
onclick: () => restoreDeleted(nameOrFile),
|
||||
},
|
||||
t.span({ className: "txt" }, "Restore"),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
t.hr({
|
||||
className: "m-t-5 m-b-0",
|
||||
hidden: () => app.utils.toArray(props.record[props.field.name]).length > 0,
|
||||
}),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm secondary block",
|
||||
title: () => local.maxReached ? "Max allowed files reached" : undefined,
|
||||
disabled: () => local.maxReached,
|
||||
onclick: (e) => {
|
||||
if (!local.maxReached) {
|
||||
fileInput.click();
|
||||
}
|
||||
document.activeElement?.blur();
|
||||
},
|
||||
},
|
||||
t.i({ className: "ri-upload-cloud-line", ariaHidden: true }),
|
||||
t.span({ className: "txt" }, "Upload or drop new file"),
|
||||
),
|
||||
),
|
||||
fieldContentEl,
|
||||
),
|
||||
() => {
|
||||
if (props.field.help) {
|
||||
@@ -251,6 +255,4 @@ export function input(props) {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return fieldEl;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export function input(props) {
|
||||
|
||||
// trigger custom change event for clearing field errors
|
||||
function triggerChangeEvent() {
|
||||
fieldEl?.dispatchEvent(
|
||||
fieldContentEl?.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { data: props },
|
||||
bubbles: true,
|
||||
@@ -87,6 +87,7 @@ export function input(props) {
|
||||
|
||||
function updateRecordValue(ids = []) {
|
||||
props.record[props.field.name] = props.field.maxSelect > 1 ? ids : ids?.[0] || "";
|
||||
triggerChangeEvent();
|
||||
}
|
||||
|
||||
const watchers = [
|
||||
@@ -96,7 +97,82 @@ export function input(props) {
|
||||
),
|
||||
];
|
||||
|
||||
const fieldEl = t.div(
|
||||
const fieldContentEl = t.output(
|
||||
{
|
||||
className: "field-content",
|
||||
name: () => props.field.name,
|
||||
},
|
||||
// loader
|
||||
t.div(
|
||||
{
|
||||
hidden: () => !local.isLoading,
|
||||
className: "list",
|
||||
},
|
||||
() => {
|
||||
const ids = app.utils.toArray(props.record[props.field.name]);
|
||||
return ids.map(() => {
|
||||
return t.div({ className: "list-item" }, t.span({ className: "skeleton-loader" }));
|
||||
});
|
||||
},
|
||||
),
|
||||
// list
|
||||
app.components.sortable({
|
||||
className: "list",
|
||||
hidden: () => local.isLoading,
|
||||
data: () => local.selected,
|
||||
onchange: (sortedList) => {
|
||||
local.selected = sortedList;
|
||||
updateRecordValue(sortedList.map((r) => r.id));
|
||||
},
|
||||
dataItem: (record, relIndex) => {
|
||||
return t.div(
|
||||
{
|
||||
rid: record,
|
||||
className: "list-item highlight",
|
||||
},
|
||||
t.div({ className: "content" }, () => app.components.recordSummary(record)),
|
||||
t.div(
|
||||
{ className: "actions" },
|
||||
t.button(
|
||||
{
|
||||
className: "btn sm secondary transparent circle",
|
||||
ariaLabel: app.attrs.tooltip("Remove"),
|
||||
onclick: () => remove(record.id),
|
||||
},
|
||||
t.i({ className: "ri-close-line", ariaHidden: true }),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
// picker btn
|
||||
t.hr({
|
||||
hidden: () => !app.utils.isEmpty(props.record[props.field.name]),
|
||||
className: "m-t-5 m-b-0",
|
||||
}),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm secondary block",
|
||||
disabled: () => local.isLoading,
|
||||
onclick: (e) => {
|
||||
app.modals.openRecordsPicker({
|
||||
collection: props.field.collectionId,
|
||||
selectedIds: app.utils.toArray(props.record[props.field.name]),
|
||||
maxSelect: props.field.maxSelect,
|
||||
onselect: (records) => {
|
||||
local.selected = records;
|
||||
updateRecordValue(records.map((r) => r.id));
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
t.i({ className: "ri-magic-line", ariaHidden: true }),
|
||||
t.span({ className: "txt" }, "Open records picker"),
|
||||
),
|
||||
);
|
||||
|
||||
return t.div(
|
||||
{
|
||||
className: "record-field-input field-type-relation",
|
||||
onunmount: () => {
|
||||
@@ -110,81 +186,7 @@ export function input(props) {
|
||||
t.i({ className: app.fieldTypes.relation.icon, ariaHidden: true }),
|
||||
t.span({ className: "txt" }, () => props.field.name),
|
||||
),
|
||||
t.output(
|
||||
{
|
||||
className: "field-content",
|
||||
name: () => props.field.name,
|
||||
},
|
||||
// loader
|
||||
t.div(
|
||||
{
|
||||
hidden: () => !local.isLoading,
|
||||
className: "list",
|
||||
},
|
||||
() => {
|
||||
const ids = app.utils.toArray(props.record[props.field.name]);
|
||||
return ids.map(() => {
|
||||
return t.div({ className: "list-item" }, t.span({ className: "skeleton-loader" }));
|
||||
});
|
||||
},
|
||||
),
|
||||
// list
|
||||
app.components.sortable({
|
||||
className: "list",
|
||||
hidden: () => local.isLoading,
|
||||
data: () => local.selected,
|
||||
onchange: (sortedList) => {
|
||||
local.selected = sortedList;
|
||||
updateRecordValue(sortedList.map((r) => r.id));
|
||||
triggerChangeEvent();
|
||||
},
|
||||
dataItem: (record, relIndex) => {
|
||||
return t.div(
|
||||
{
|
||||
rid: record,
|
||||
className: "list-item highlight",
|
||||
},
|
||||
t.div({ className: "content" }, () => app.components.recordSummary(record)),
|
||||
t.div(
|
||||
{ className: "actions" },
|
||||
t.button(
|
||||
{
|
||||
className: "btn sm secondary transparent circle",
|
||||
ariaLabel: app.attrs.tooltip("Remove"),
|
||||
onclick: () => remove(record.id),
|
||||
},
|
||||
t.i({ className: "ri-close-line", ariaHidden: true }),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
// picker btn
|
||||
t.hr({
|
||||
hidden: () => !app.utils.isEmpty(props.record[props.field.name]),
|
||||
className: "m-t-5 m-b-0",
|
||||
}),
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
className: "btn sm secondary block",
|
||||
disabled: () => local.isLoading,
|
||||
onclick: (e) => {
|
||||
app.modals.openRecordsPicker({
|
||||
collection: props.field.collectionId,
|
||||
selectedIds: app.utils.toArray(props.record[props.field.name]),
|
||||
maxSelect: props.field.maxSelect,
|
||||
onselect: (records) => {
|
||||
local.selected = records;
|
||||
updateRecordValue(records.map((r) => r.id));
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
t.i({ className: "ri-magic-line", ariaHidden: true }),
|
||||
t.span({ className: "txt" }, "Open records picker"),
|
||||
),
|
||||
),
|
||||
fieldContentEl,
|
||||
),
|
||||
() => {
|
||||
if (props.field.help) {
|
||||
@@ -192,6 +194,4 @@ export function input(props) {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return fieldEl;
|
||||
}
|
||||
Reference in new issue
Block a user