1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-17 10:22:36 +00:00

Resolve "Create html/scss/javascript code for the page editor"

This commit is contained in:
Bram Wiepjes 2023-02-23 10:02:58 +00:00
parent f1a469946f
commit 46392a1919
9 changed files with 872 additions and 20 deletions
web-frontend/modules

View file

@ -0,0 +1,349 @@
<template>
<div>
<div style="padding: 30px">
<header>
<nav>
<p>HEADER</p>
<menu id="navmenu" type="context">
<menuitem label="Home" icon="icon.png">
<a href="#">Home</a>
</menuitem>
</menu>
</nav>
</header>
</div>
<slot></slot>
<div style="padding: 30px">
<main>
<h1>Heading...</h1>
<h2>Heading...</h2>
<h3>Heading...</h3>
<h4>Heading...</h4>
<h5>Heading...</h5>
<h6>Heading...</h6>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus
nisi lacus, auctor sit amet purus vel, gravida luctus lectus. Aenean
rhoncus dapibus enim, sit amet faucibus leo ornare vitae. <br />
<span> span </span>
<b>Bold word</b>
<i>italic</i>
<em>emphasis</em>
<mark>mark</mark>
<small> small </small>
<sub> sub </sub>
<sup> sup </sup>
<u> Statements... </u>
<abbr title="National Aeronautics and Space Administration"
>NASA</abbr
>
<strike> strikethrough </strike>
<span><del> deprecated info </del> <ins> new info </ins> </span>
<s> not relevant </s>
<a href="#link">link</a>
<time datetime="2020-08-17 08:00">Monday at 8:00 AM</time>
<br />
<kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>CANC</kbd>
</p>
</main>
<p>This is a <q>short quote</q></p>
<blockquote>
This instead is a long quote that is going to use a lot of words and
also cite who said that. <cite>Some People</cite>
</blockquote>
<ol>
<li><data value="21053">data tag</data></li>
<li><data value="23452">data tag</data></li>
<li><data value="42545">data tag</data></li>
<li>List item</li>
<li>List item</li>
<li>List item</li>
</ol>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
<li>List item</li>
<li>List item</li>
<li>List item</li>
</ul>
<hr />
<video
width="100%"
height="480"
style="max-width: 100%"
src="https://archive.org/download/Popeye_forPresident/Popeye_forPresident_512kb.mp4"
controls
>
<track kind="subtitles" src="subtitles_de.vtt" srclang="de" />
<track kind="subtitles" src="subtitles_en.vtt" srclang="en" />
<track kind="subtitles" src="subtitles_ja.vtt" srclang="ja" />
Sorry, your browser doesn't support HTML5 <code>video</code>, but you
can download this video from the
<a
href="https://archive.org/details/Popeye_forPresident"
target="_blank"
>Internet Archive</a
>.
</video>
<object
data="flashmovie.swf"
width="600"
height="800"
type="application/x-shockwave-flash"
>
Please install the Shockwave plugin to watch this movie.
</object>
<pre style="max-width: 100%; overflow: scroll">
_,'/
_.-''._:
,-:`-.-' .:.|
;-.'' .::.|
_..------.._ / (:. .:::.|
,'. .. . . .`/ : :. .::::.|
,'. . . . ./ \ ::. .::::::.|
,'. . . . . / `.,,::::::::.;\
/ . . / ,',';_::::::,:_:
/ . . . . / ,',','::`--'':;._;
: . . / ,',',':::::::_:'_,'
|.. . . . / ,',','::::::_:'_,'
|. /,-. /,',':::::_:'_,'
| .. . . /) /-:/,'::::_:',-'
: . . . // / ,'):::_:',' ;
\ . . // /,' /,-.',' ./
\ . . `::./,// ,'' ,' . /
`. . . `;;;,/_.'' . . ,'
,`. . :;;' `:. . ,'
/ `-._,' .. ` _.-'
( _,'``------''
`--''
</pre>
<p>
<var> variable </var> = 1000;
<samp
>Traceback (most recent call last):<br />NameError: name 'variabl' is
not defined</samp
>
</p>
<table>
<thead>
<tr>
<th>Numbers</th>
<th>Letters</th>
<th>Colors</th>
</tr>
</thead>
<tfoot>
<tr>
<td>123</td>
<td>ABC</td>
<td>RGB</td>
</tr>
</tfoot>
<tbody>
<tr>
<td>1</td>
<td>A</td>
<td>Red</td>
</tr>
<tr>
<td>2</td>
<td>B</td>
<td>Green</td>
</tr>
<tr>
<td>3</td>
<td>C</td>
<td>Blue</td>
</tr>
</tbody>
</table>
<p>
A <dfn>definition</dfn> is an explanation of the meaning of a word or
phrase.
</p>
<details>
<summary>Summary of content below</summary>
<p>Content 1</p>
<p>Content 2</p>
<p>Content 3</p>
<p>Content 4</p>
</details>
<section>
<h1>Content</h1>
<p>Informations about content.</p>
</section>
<progress value="33" max="100"></progress>
<meter value="11" min="0" max="45" optimum="40">25 out of 45</meter>
<p>2+2 = <output>4</output></p>
<select>
<optgroup label="Choice [1-3]">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</optgroup>
<optgroup label="Choice [4-6]">
<option value="4">Four</option>
<option value="5">Five</option>
<option value="6">Six</option>
</optgroup>
</select>
<div>
<div>
<p>div > div > p</p>
</div>
<br />
</div>
<svg width="100" height="100">
<circle
cx="50"
cy="50"
r="40"
stroke="green"
stroke-width="4"
fill="yellow"
/>
</svg>
<br />
<textarea
id="textarea"
name="textarea"
rows="4"
cols="50"
style="max-width: 100%"
>
Write something in here
</textarea>
<br />
<audio controls>
I'm sorry. You're browser doesn't support HTML5 <code>audio</code>.
<source
src="https://archive.org/download/ReclaimHtml5/ReclaimHtml5.ogg"
type="audio/ogg"
/>
<source
src="https://archive.org/download/ReclaimHtml5/ReclaimHtml5.mp3"
type="audio/mpeg"
/>
</audio>
<p>
This is a recording of a talk called <cite>Reclaim HTML5</cite> which
was orinally delieved in Vancouver at a
<a
href="http://www.meetup.com/vancouver-javascript-developers/"
taget="_blank"
>Super VanJS Meetup</a
>. It is hosted by
<a href="https://archive.org/details/ReclaimHtml5" target="_blank"
>The Internet Archive</a
>
and licensed under
<a
href="http://creativecommons.org/licenses/by/3.0/legalcode"
target="_blank"
>CC 3.0</a
>.
</p>
<iframe
src="https://open.spotify.com/embed?uri=spotify%3Atrack%3A67HxeUADW4H3ERfaPW59ma?si=PogFcGg9QqapyoPbn2lVOw"
width="300"
height="380"
frameborder="0"
allowtransparency="true"
></iframe>
<article>
<header>
<h2>Title of Article</h2>
<span>by Arthur T. Writer</span>
</header>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
volutpat sollicitudin nisi, at convallis nunc semper et. Donec
ultrices odio ac purus facilisis, at mollis urna finibus.
</p>
<figure>
<img
src="https://placehold.it/600x300"
alt="placeholder-image"
style="max-width: 100%"
/>
<figcaption>Caption.</figcaption>
</figure>
<footer>
<dl>
<dt>Published</dt>
<dd>17 August 2020</dd>
<dt>Tags</dt>
<dd>Sample Posts, html example</dd>
</dl>
</footer>
</article>
<form>
<fieldset>
<legend>Personal Information</legend>
<label for="name">Name</label><br />
<input id="name" name="name" /><br />
<label for="dob">Date of Birth</label><br />
<input id="dob" name="dob" type="date" />
</fieldset>
</form>
<aside>
<p>P inside ASIDE tag</p>
</aside>
<map name="shapesmap">
<area shape="rect" coords="29,32,230,215" href="#square" alt="Square" />
<area shape="circle" coords="360,130,100" href="#circle" alt="Circle" />
</map>
<img
src="https://placehold.it/100x100"
alt="placeholder-image"
style="max-width: 100%"
/>
<form action="" method="get">
<label for="browser">Choose your browser from the list:</label>
<input id="browser" list="browsers" name="browser" />
<datalist id="browsers">
<option value="Edge" />
<option value="Firefox" />
<option value="Chrome" />
<option value="Opera" />
<option value="Safari" />
</datalist>
<input type="submit" />
</form>
<footer>
<address>
relevant contacts <a href="mailto:mail@example.com">mail</a>.
</address>
<div>created by <a href="https://blazardsky.space">@blazardsky</a></div>
</footer>
</div>
</div>
</template>

View file

@ -3,7 +3,7 @@
:default-values="{ name: defaultName }"
@submitted="$emit('submitted', $event)"
>
<FormElement class="builder-form__controls">
<div class="actions actions--right">
<button
class="button button--large"
:class="{ 'button--loading': loading }"
@ -12,7 +12,7 @@
>
{{ $t('builderForm.submit') }}
</button>
</FormElement>
</div>
</ApplicationForm>
</template>

View file

@ -0,0 +1,285 @@
<template>
<div>
<!--
This is a temporary page that just holds the static html code. It can later be used
to copy parts out off when building the real thing. When we don't need it anymore,
we should delete it. Note that when this page component is deleted, we must also
delete `builder/components/TemporaryPreview.vue`.
-->
<header class="layout__col-2-1 header header--space-between">
<ul class="header__filter">
<li class="header__filter-item">
<div>
<a class="header__filter-link"
><i class="header__filter-icon fas fa-stream"></i>
<span class="header__filter-name">Elements</span></a
>
</div>
</li>
<li class="header__filter-item">
<div>
<a class="header__filter-link"
><i class="header__filter-icon fas fa-table"></i>
<span class="header__filter-name">Data</span></a
>
</div>
</li>
<li class="header__filter-item">
<div>
<a class="header__filter-link"
><i class="header__filter-icon fas fa-square-root-alt"></i>
<span class="header__filter-name">Variables</span></a
>
</div>
</li>
<li class="header__filter-item">
<div>
<a class="header__filter-link"
><i class="header__filter-icon fas fa-cogs"></i>
<span class="header__filter-name">Page settings</span></a
>
</div>
</li>
</ul>
<ul class="header__filter">
<li class="header__filter-item header__filter-item--no-margin-left">
<div>
<a
class="header__filter-link"
:class="{ 'active active--primary': device === 'desktop' }"
@click="setDevice('desktop')"
><i class="header__filter-icon fas fa-laptop"></i
></a>
</div>
</li>
<li class="header__filter-item">
<div>
<a
class="header__filter-link"
:class="{ 'active active--primary': device == 'tablet' }"
@click="setDevice('tablet')"
><i class="header__filter-icon fas fa-tablet"></i
></a>
</div>
</li>
<li class="header__filter-item">
<div>
<a
class="header__filter-link"
:class="{ 'active active--primary': device == 'smartphone' }"
@click="setDevice('smartphone')"
><i class="header__filter-icon fas fa-mobile"></i
></a>
</div>
</li>
</ul>
<ul class="header__filter">
<li class="header__filter-item">
<div>
<a class="header__filter-link">
<span class="header__filter-name">Preview</span></a
>
</div>
</li>
<li class="header__filter-item header__filter-item--right">
<div>
<a class="header__filter-link"
><i class="header__filter-icon fas fa-upload"></i>
<span class="header__filter-name">Publish</span></a
>
</div>
</li>
</ul>
</header>
<div class="layout__col-2-2 content">
<div class="page-builder">
<div class="page-builder__preview-wrapper">
<div
ref="preview"
class="page-builder__preview"
:class="{
'page-builder__preview--tablet': device == 'tablet',
'page-builder__preview--smartphone': device == 'smartphone',
}"
>
<div ref="previewScaled" class="page-builder__preview-scaled">
<TemporaryPreview>
<div
v-for="(i, index) in new Array(3)"
:key="index"
class="page-builder__preview-element"
:class="{
'page-builder__preview-element--active':
selectedElementIndex === index,
}"
@click="selectedElementIndex = index"
>
<a
v-if="selectedElementIndex + 1 !== index"
href="#"
class="
page-builder__preview-add
page-builder__preview-element-add
page-builder__preview-element-add-first
"
>
<i class="fas fa-plus"></i>
</a>
<a
v-if="selectedElementIndex - 1 !== index"
href="#"
class="
page-builder__preview-add
page-builder__preview-element-add
page-builder__preview-element-add-last
"
>
<i class="fas fa-plus"></i>
</a>
<div
class="
page-builder__preview-menu
page-builder__preview-element-menu
"
>
<a href="#" class="page-builder__preview-menu-item">
<i class="fas fa-copy"></i>
<div class="page-builder__preview-menu-item-description">
Copy
</div>
</a>
<a href="#" class="page-builder__preview-menu-item">
<i class="fas fa-arrow-up"></i>
<div class="page-builder__preview-menu-item-description">
Move up
</div>
</a>
<a href="#" class="page-builder__preview-menu-item">
<i class="fas fa-arrow-down"></i>
<div class="page-builder__preview-menu-item-description">
Move down
</div>
</a>
<a href="#" class="page-builder__preview-menu-item">
<i class="fas fa-trash"></i>
<div class="page-builder__preview-menu-item-description">
Delete
</div>
</a>
</div>
<p style="font-size: 20px; line-height: 180%; margin: 0">
1Are your projects, ideas or business processes unorganized
or unclear? With Baserow you decide how you want to
structure everything. Whether youre managing customers,
products or airplanes. If you know how a spreadsheet works,
you know how Baserow works.
</p>
</div>
</TemporaryPreview>
</div>
</div>
</div>
<div class="page-builder__sidebar">
<ul class="page-builder__sidebar-tabs">
<li class="page-builder__sidebar-tab">
<a href="#" class="page-builder__sidebar-tab-link">General</a>
</li>
<li class="page-builder__sidebar-tab">
<a href="#" class="page-builder__sidebar-tab-link">Style</a>
</li>
<li class="page-builder__sidebar-tab">
<a
href="#"
class="
page-builder__sidebar-tab-link
page-builder__sidebar-tab-link--active
"
>Visibility</a
>
</li>
<li class="page-builder__sidebar-tab">
<a href="#" class="page-builder__sidebar-tab-link">Events</a>
</li>
</ul>
<div class="page-builder__sidebar-content">
content
<br />
content
<br />
content
<br />
content
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ResizeObserver from 'resize-observer-polyfill'
import TemporaryPreview from '@baserow/modules/builder/components/TemporaryPreview'
export default {
name: 'Page',
components: { TemporaryPreview },
layout: 'app',
data() {
return {
device: 'desktop', // desktop, tablet or smartphone
selectedElementIndex: 1,
}
},
mounted() {
this.resizeObserver = new ResizeObserver(() => {
this.resized()
})
this.resizeObserver.observe(this.$el)
this.$nextTick(() => {
this.resized()
})
},
beforeDestroy() {
this.resizeObserver.unobserve(this.$el)
},
methods: {
setDevice(value) {
this.device = value
this.$nextTick(() => {
this.resized()
})
},
resized() {
// The widths are the minimum width the preview must have. If the preview dom
// element becomes smaller than the target, it will be scaled down so that the
// actual width remains the same, and it will preview the correct device.
const minimumWidths = {
desktop: 1100,
tablet: 768,
smartphone: 420,
}
const preview = this.$refs.preview
const previewScaled = this.$refs.previewScaled
const currentWidth = preview.clientWidth
const currentHeight = preview.clientHeight
const targetWidth = minimumWidths[this.device]
let scale = 1
let horizontal = 0
let vertical = 0
if (currentWidth < targetWidth) {
scale = Math.round((currentWidth / targetWidth) * 100) / 100
horizontal = (currentWidth - currentWidth * scale) / 2 / scale
vertical = (currentHeight - currentHeight * scale) / 2 / scale
}
previewScaled.style.transform = `scale(${scale})`
previewScaled.style.transformOrigin = `0 0`
previewScaled.style.width = `${horizontal * 2 + currentWidth}px`
previewScaled.style.height = `${vertical * 2 + currentHeight}px`
},
},
}
</script>

View file

@ -1 +1,9 @@
export const routes = []
import path from 'path'
export const routes = [
{
name: 'tmp-app-builder-page',
path: '/tmp-app-builder-page',
component: path.resolve(__dirname, 'pages/tmpPageBuilder.vue'),
},
]

View file

@ -1 +1 @@
@import 'builder_form';
@import 'page_builder';

View file

@ -1,4 +0,0 @@
.builder-form__controls {
display: flex;
justify-content: flex-end;
}

View file

@ -0,0 +1,210 @@
.page-builder {
@include absolute(0, 0, 0, 0);
}
.page-builder__preview-wrapper {
@include absolute(30px, 400px, 0, 40px);
display: flex;
justify-content: center;
}
.page-builder__preview {
position: relative;
z-index: 1;
background-color: $white;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
width: 100%;
height: 100%;
overflow: hidden;
&.page-builder__preview--tablet {
max-width: 768px;
}
&.page-builder__preview--smartphone {
max-width: 420px;
}
}
.page-builder__preview-add {
@include center-text(26px, 10px);
display: block;
border-radius: 100%;
border: solid 1px $color-neutral-300;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.16);
color: $color-primary-900;
background-color: $white;
&:hover {
background-color: $color-neutral-50;
box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.32);
}
}
.page-builder__preview-menu {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
width: 26px;
border: solid 1px $color-neutral-400;
border-radius: 3px;
}
.page-builder__preview-menu-item-description {
@include absolute(2px, 30px, auto, auto);
display: none;
background-color: $color-neutral-900;
font-size: 11px;
color: $white;
line-height: 20px;
padding: 0 4px;
border-radius: 3px;
white-space: nowrap;
}
.page-builder__preview-menu-item {
@include center-text(24px, 9px);
position: relative;
background-color: $white;
color: $color-primary-900;
&:hover {
background-color: $color-neutral-100;
.page-builder__preview-menu-item-description {
display: block;
}
}
&:first-child {
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
&:last-child {
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
}
.page-builder__preview-element {
position: relative;
.page-builder__preview-element-add,
.page-builder__preview-menu {
display: none;
}
&:hover {
.page-builder__preview-element-add {
display: block;
}
.page-builder__preview-menu {
display: flex;
}
}
&:not(.page-builder__preview-element--active) {
cursor: pointer;
}
&.page-builder__preview-element--active {
cursor: inherit;
&::before {
@include absolute(0, 0, 0, 0);
content: "";
border: solid 1px $color-primary-500;
pointer-events: none;
}
.page-builder__preview-element-add {
display: block;
}
}
}
.page-builder__preview-element-add-first,
.page-builder__preview-element-add-last {
@include absolute(-13px, auto, auto, 50%);
margin-left: -12px;
z-index: 2;
}
.page-builder__preview-element-add-last {
top: auto;
bottom: -12px;
}
.page-builder__preview-element-menu {
@include absolute(6px, 6px, auto, auto);
}
.page-builder__preview-scaled {
width: 100%;
height: 100%;
overflow: auto;
}
.page-builder__sidebar {
@include absolute(0, 0, 0, auto);
width: 360px;
background-color: $white;
border-left: solid 1px $color-neutral-200;
}
.page-builder__sidebar-tabs {
@include absolute(0, 0, auto, 0);
list-style: none;
margin: 0;
padding: 0 16px;
display: flex;
gap: 16px;
border-bottom: solid 1px $color-neutral-200;
}
.page-builder__sidebar-tab {
// nothing
}
.page-builder__sidebar-tab-link {
position: relative;
display: block;
line-height: 42px;
color: $color-primary-900;
&::after {
@include absolute(auto, 0, -1px, 0);
content: "";
border-bottom: solid 1px transparent;
}
&:hover {
text-decoration: none;
&::after {
border-bottom-color: $color-neutral-400;
}
}
&.page-builder__sidebar-tab-link--active::after {
border-bottom-color: $color-primary-500;
}
}
.page-builder__sidebar-content {
@include absolute(43px, 0, 0, 0);
overflow-y: auto;
}

View file

@ -3,6 +3,10 @@
justify-content: left;
background-color: $white;
border-bottom: 2px solid $color-neutral-200;
&.header--space-between {
justify-content: space-between;
}
}
@keyframes header-loading-loop {

View file

@ -19,19 +19,19 @@
{{ $t('error.requiredField') }}
</div>
</div>
<div class="actions">
<div class="align-right">
<button
class="button button--large"
:class="{ 'button--loading': loading }"
:disabled="loading"
>
{{ $t('action.add') }}
{{ databaseApplicationType.getName() | lowercase }}
</button>
</div>
</div>
</FormElement>
<div class="actions">
<div class="align-right">
<button
class="button button--large"
:class="{ 'button--loading': loading }"
:disabled="loading"
>
{{ $t('action.add') }}
{{ databaseApplicationType.getName() | lowercase }}
</button>
</div>
</div>
</form>
</template>