Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F12656368
jdd-page.ts
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
jdd-page.ts
View Options
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import
type
{
Readable
}
from
"node:stream"
;
import
type
{
Component
,
JDDContext
,
RawJDDocument
}
from
"@sealcode/jdd"
;
import
{
documentContainerFromParsed
,
Registry
}
from
"@sealcode/jdd"
;
import
{
JDD
}
from
"@sealcode/jdd"
;
import
{
StatefulPage
}
from
"@sealcode/sealgen"
;
import
{
hasFieldOfType
,
hasShape
,
predicates
}
from
"@sealcode/ts-predicates"
;
import
type
{
Context
}
from
"koa"
;
import
type
{
FlatTemplatable
,
Templatable
}
from
"tempstream"
;
import
{
tempstream
}
from
"tempstream"
;
import
{
ComponentInput
}
from
"./inputs/component-input.js"
;
import
{
ComponentPreviewActions
}
from
"./component-preview-actions.js"
;
export
const
actionName
=
"Components"
;
export
type
JDDPageState
=
{
components
:
RawJDDocument
;
preview_size
?:
string
;
messages
?:
string
[];
};
export
default
abstract
class
JDDPage
extends
StatefulPage
<
JDDPageState
,
typeof
ComponentPreviewActions
>
{
actions
=
ComponentPreviewActions
;
previewSizes
=
[
"320"
,
"600"
,
"800"
,
"1024"
,
"1300"
,
"1920"
];
classes
:
string
[]
=
[];
public
registry
:
Registry
;
public
makeJDDContext
:
(
ctx
:
Context
)
=>
JDDContext
;
public
html
:
(
args
:
unknown
)
=>
string
|
Promise
<
string
>
|
Readable
|
Promise
<
Readable
>
;
public
defaultHead
:
(
...
args
:
unknown
[]
)
=>
string
|
Promise
<
string
>
|
Readable
|
Promise
<
Readable
>
;
public
makeAssetURL
:
(
asset
:
string
)
=>
string
;
constructor
(
args
:
{
registry
:
Registry
;
makeJDDContext
:
(
ctx
:
Context
)
=>
JDDContext
;
html
:
(
args
:
unknown
)
=>
string
|
Promise
<
string
>
|
Readable
|
Promise
<
Readable
>
;
defaultHead
:
(
...
args
:
unknown
[]
)
=>
string
|
Promise
<
string
>
|
Readable
|
Promise
<
Readable
>
;
makeAssetURL
?:
(
asset
:
string
)
=>
string
;
})
{
super
();
this
.
registry
=
args
.
registry
;
this
.
makeJDDContext
=
args
.
makeJDDContext
;
this
.
defaultHead
=
args
.
defaultHead
;
this
.
html
=
args
.
html
;
this
.
makeAssetURL
=
args
.
makeAssetURL
||
((
str
)
=>
`/dist/jdd-page/
${
str
.
startsWith
(
"/"
)
?
str
.
slice
(
1
)
:
str
}
`
);
}
getRegistryComponents
()
{
return
this
.
registry
.
getAll
();
}
async
getInitialState
(
ctx
:
Context
)
{
const
all_components
=
Object
.
entries
(
this
.
getRegistryComponents
());
const
first_component
=
all_components
[
0
];
if
(
!
first_component
)
{
throw
new
Error
(
"No defined components!"
);
}
const
[
component_name
,
component
]
=
first_component
;
const
initial_state
=
{
components
:
[
{
component_name
:
component_name
,
args
:
await
component
.
getExampleValues
(
this
.
makeJDDContext
(
ctx
)
),
},
],
};
return
initial_state
;
}
wrapInLayout
(
ctx
:
Context
,
content
:
Templatable
,
state
:
JDDPageState
)
:
Templatable
{
const
jdd
=
new
JDD
(
this
.
registry
,
this
.
makeJDDContext
(
ctx
),
documentContainerFromParsed
(
state
.
components
)
);
return
this
.
html
({
ctx
,
title
:
"Components"
,
body
:
content
,
description
:
""
,
css_clumps
:
[
"jdd-page"
,
...
jdd
.
getAllCSSClumps
()],
htmlOptions
:
{
morphing
:
true
,
preserveScroll
:
true
,
autoRefreshCSS
:
false
,
navbar
:
()
=>
``
,
bodyClasses
:
[
"jdd-editor"
],
showBottomNavbar
:
false
,
showBanner
:
false
,
showFooter
:
false
,
loadHamburgerMenu
:
false
,
loadSearchModal
:
false
,
},
makeHead
:
(...
args
:
unknown
[])
=>
tempstream
/* HTML */
`
${
this
.
defaultHead
(...
args
)
}
<link
href="/dist/jdd-page.entrypoint.css"
rel="stylesheet"
type="text/css"
/>
${
jdd
.
renderEarlyAssets
()
}
`
,
});
}
async
preprocessOverrides
(
_ctx
:
Context
,
state
:
JDDPageState
,
overrides
:
Record
<
string
,
unknown
>
)
{
const
jdd_context
=
this
.
makeJDDContext
(
_ctx
);
if
(
!
hasFieldOfType
(
"components"
,
overrides
,
predicates
.
array
(
predicates
.
shape
({
args
:
predicates
.
object
,
})
)
)
)
{
return
{};
}
for
(
const
[
component_index
,
{
component_name
}]
of
Object
.
entries
(
state
.
components
))
{
const
component
=
this
.
registry
.
get
(
component_name
);
if
(
!
component
)
{
throw
new
Error
(
`Unknown component:
${
component_name
}
`
);
}
const
overrides_for_component
=
overrides
.
components
[
parseInt
(
component_index
)
]
||
{
args
:
{}
};
const
promises
=
Object
.
entries
(
component
.
getArguments
()).
map
(
async
([
arg_name
,
arg
])
=>
{
const
value
=
overrides_for_component
.
args
[
arg_name
];
if
(
value
)
{
const
new_value
=
await
arg
.
receivedToParsed
(
jdd_context
,
value
);
overrides_for_component
.
args
[
arg_name
]
=
new_value
;
}
}
);
// eslint-disable-next-line no-await-in-loop
await
Promise
.
all
(
promises
);
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return
overrides
;
}
// eslint-disable-next-line no-unused-vars
abstract
renderParameterButtons
(
_state
:
JDDPageState
)
:
FlatTemplatable
;
renderComponentArgs
<
C
extends
Component
>
(
ctx
:
Context
,
state
:
JDDPageState
,
component
:
C
,
args
:
Record
<
string
,
unknown
>
,
index
:
number
)
:
FlatTemplatable
{
const
jdd_context
=
this
.
makeJDDContext
(
ctx
);
return
tempstream
/* HTML */
`<div
class="component-preview-parameters"
id="
${
`component-preview-parameters--
${
index
}
`
}
"
>
${
Object
.
entries
(
component
.
getArguments
()).
map
(
async
([
arg_name
,
arg
])
=>
ComponentInput
({
state
,
arg_path
:
[
"components"
,
index
.
toString
(),
"args"
,
arg_name
,
],
ctx
,
arg
,
value
:
args
[
arg_name
]
===
undefined
?
arg
.
getExampleValue
(
jdd_context
)
:
args
[
arg_name
],
page
:
this
,
makeJDDContext
:
this
.
makeJDDContext
,
makeAssetURL
:
this
.
makeAssetURL
,
}
)
)}
</div>`
;
}
renderComponentBlock
(
ctx
:
Context
,
state
:
JDDPageState
,
{
component_name
,
args
:
component_args
,
}
:
{
component_name
:
string
;
args
:
Record
<
string
,
unknown
>
;
},
component_index
:
number
)
{
const
component
=
this
.
registry
.
get
(
component_name
);
if
(
!
component
)
{
return
null
;
}
return
this
.
renderComponentArgs
(
ctx
,
state
,
component
,
component_args
,
component_index
);
}
async
serializeState
(
ctx
:
Context
,
state
:
JDDPageState
,
pretty
=
false
)
{
const
serialized_components
=
await
Promise
.
all
(
state
.
components
.
map
(
async
({
component_name
,
args
})
=>
{
const
component
=
this
.
registry
.
get
(
component_name
);
const
single_result
=
{
component_name
,
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
args
:
component
?
await
component
.
convertParsedToStorage
(
this
.
makeJDDContext
(
ctx
),
args
)
:
{},
};
return
single_result
;
})
);
const
serialized_state
=
JSON
.
stringify
(
{
...
state
,
components
:
serialized_components
},
null
,
pretty
?
4
:
""
);
return
serialized_state
;
}
async
deserializeState
(
ctx
:
Context
,
state_string
:
string
)
{
const
jdd_context
=
this
.
makeJDDContext
(
ctx
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const
raw
=
JSON
.
parse
(
state_string
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const
components_storage
=
raw
.
components
;
if
(
!
Array
.
isArray
(
components_storage
))
{
throw
new
Error
(
"'components' key is not an array, got ${components_storage}"
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const
components_parsed
=
await
Promise
.
all
(
components_storage
.
map
(
async
(
entry
)
=>
{
if
(
!
hasShape
(
{
component_name
:
predicates
.
string
,
args
:
predicates
.
object
,
},
entry
)
)
{
throw
new
Error
(
`Expected components[] items to be objects with 'component_name' and 'args' keys, got
${
entry
}
`
);
}
const
{
component_name
,
args
}
=
entry
;
const
component
=
this
.
registry
.
get
(
component_name
);
if
(
!
component
)
{
throw
new
Error
(
"Unknown component: ${component_name}"
);
}
return
{
component_name
,
args
:
await
component
.
convertStorageToParsed
(
jdd_context
,
args
),
};
})
);
const
result
=
{
...
raw
,
components
:
components_parsed
};
return
result
;
}
renderPreParameterButtons
(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_ctx
:
Context
,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_state
:
JDDPageState
)
:
FlatTemplatable
|
Promise
<
FlatTemplatable
>
{
return
""
;
}
renderMessages
(
_ctx
:
Context
,
state
:
JDDPageState
)
{
return
/* HTML */
`<ul
class="jdd-editor__messages"
data-controller="toast"
>
${
(
state
.
messages
||
[]).
map
(
(
e
)
=>
`<li class="jdd-editor__message">
${
e
}
</li>`
)
}
</ul>`
;
}
async
render
(
ctx
:
Context
,
state
:
JDDPageState
)
:
Promise
<
string
>
{
return
tempstream
/* HTML */
`<div
class="
${
[
"two-column"
,
"component-debugger"
,
...
this
.
classes
].
join
(
" "
)
}
"
id="component-debugger"
style="
${
`--resizable-column-width:
${
state
.
preview_size
?
state
.
preview_size
+
"px"
:
"50vw"
}
`
}
"
data-controller="component-debugger"
>
<div class="component-arguments" id="component-arguments">
${
this
.
renderPreParameterButtons
(
ctx
,
state
)
}
${
this
.
renderParameterButtons
(
state
)
}
${
this
.
renderMessages
(
ctx
,
state
)
}
${
state
.
components
.
map
((
component
,
component_index
)
=>
this
.
renderComponentBlock
(
ctx
,
state
,
component
,
component_index
)
)
}
<details
class="component-debugger__json"
data-controller="exportable-textarea"
id="exportable-textarea"
open
>
<summary>Edit/Export raw JSON</summary>
<textarea
name="state_override"
rows="40"
cols="40"
data-controller="json-editor"
id="component-debugger-json-textarea"
autocomplete="off"
>
${
(
await
this
.
serializeState
(
ctx
,
state
,
true
)).
replaceAll
(
"<"
,
"<"
)
}
</textarea
>
${
this
.
makeActionButton
(
state
,
{
action
:
"replace_state"
,
label
:
"Apply"
,
}
)}
<button data-action="exportable-textarea#copy">Copy</button>
<button data-action="exportable-textarea#download">
Download
</button>
<input type="file" />
${
" "
}
<button data-action="exportable-textarea#import">
Import
</button>
</details>
</div>
<div
id="resize-gutter"
class="resize-gutter"
data-component-debugger-target="gutter"
></div>
<div
id="component-preview"
class="component-preview"
data-component-debugger-target="preview"
>
<div
id="component-preview__header"
class="component-preview__header"
>
<span>Preview</span>
<select
name="$[preview_size]"
autocomplete="off"
class="component-preview-size-select"
data-component-debugger-target="sizeSelect"
data-action="change->component-debugger#handleWidthDropdown"
data-turbo-data-turbo-permanent
>
${
state
.
preview_size
?
/* HTML */
`<option
class="dynamic"
value="
${
state
.
preview_size
}
"
selected
>
${
state
.
preview_size
}
px
</option>`
:
""
}
${
this
.
previewSizes
.
map
(
(
size
)
=>
/* HTML */
`<option value="
${
size
}
">
${
`
${
size
}
px`
}
</option>`
)
}
</select>
<noscript>
${
this
.
makeActionButton
(
state
,
"change_size"
)
}
</noscript>
</div>
<div class="jdd-outer-container">
<div class="jdd-container">
${
JDD
.
render
(
this
.
registry
,
documentContainerFromParsed
(
state
.
components
),
this
.
makeJDDContext
(
ctx
)
)
}
</div>
</div>
</div>
</div>`
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-java
Expires
Fri, Nov 28, 15:11 (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1063639
Default Alt Text
jdd-page.ts (11 KB)
Attached To
Mode
rJDDE jdd-editor
Attached
Detach File
Event Timeline
Log In to Comment