Add the File > Export dialog and PNG/JPG downloading (#629)

* Add export dialog

* Code review changes

* More code review feedback

* Fix compilation on stable Rust

* Fixes to problems

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2022-05-09 07:35:22 +01:00
committed by Keavon Chambers
co-authored by Keavon Chambers
parent e3e506ecfb
commit 060182fd31
19 changed files with 445 additions and 59 deletions
@@ -4,6 +4,7 @@
<!-- TODO: Use `<component :is="" v-bind="attributesObject"></component>` to avoid all the separate components with `v-if` -->
<CheckboxInput v-if="component.kind === 'CheckboxInput'" v-bind="component.props" @update:checked="(value: boolean) => updateLayout(component.widget_id, value)" />
<ColorInput v-if="component.kind === 'ColorInput'" v-bind="component.props" @update:value="(value: string) => updateLayout(component.widget_id, value)" />
<DropdownInput v-if="component.kind === 'DropdownInput'" v-bind="component.props" @update:selectedIndex="(value: number) => updateLayout(component.widget_id, value)" />
<FontInput
v-if="component.kind === 'FontInput'"
v-bind="component.props"
@@ -69,6 +70,7 @@ import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
import TextButton from "@/components/widgets/buttons/TextButton.vue";
import CheckboxInput from "@/components/widgets/inputs/CheckboxInput.vue";
import ColorInput from "@/components/widgets/inputs/ColorInput.vue";
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
import FontInput from "@/components/widgets/inputs/FontInput.vue";
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";
import OptionalInput from "@/components/widgets/inputs/OptionalInput.vue";
@@ -104,6 +106,7 @@ export default defineComponent({
IconButton,
OptionalInput,
RadioInput,
DropdownInput,
TextLabel,
IconLabel,
ColorInput,
@@ -22,7 +22,7 @@
width: 100%;
height: 100%;
.floating-menu-container .floating-menu-content {
> .floating-menu-container > .floating-menu-content {
pointer-events: auto;
padding: 24px;
}
@@ -50,7 +50,7 @@
.main-column {
margin: -4px 0;
.details {
.details.text-label {
user-select: text;
white-space: pre-wrap;
max-width: 400px;
@@ -114,7 +114,7 @@
justify-content: center;
align-items: center;
.floating-menu-content {
> .floating-menu-container > .floating-menu-content {
transform: translate(-50%, -50%);
}
}
@@ -221,20 +221,24 @@ export default defineComponent({
this.floatingMenuBounds = floatingMenu.getBoundingClientRect();
this.floatingMenuContentBounds = floatingMenuContent.getBoundingClientRect();
// Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping)
const tailOffset = this.type === "Popover" ? 10 : 0;
if (this.direction === "Bottom") floatingMenuContent.style.top = `${tailOffset + this.floatingMenuBounds.top}px`;
if (this.direction === "Top") floatingMenuContent.style.bottom = `${tailOffset + this.floatingMenuBounds.bottom}px`;
if (this.direction === "Right") floatingMenuContent.style.left = `${tailOffset + this.floatingMenuBounds.left}px`;
if (this.direction === "Left") floatingMenuContent.style.right = `${tailOffset + this.floatingMenuBounds.right}px`;
const inParentFloatingMenu = Boolean(floatingMenuContainer.closest("[data-floating-menu-content]"));
// Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping)
const tail = this.$refs.tail as HTMLElement;
if (tail) {
if (this.direction === "Bottom") tail.style.top = `${this.floatingMenuBounds.top}px`;
if (this.direction === "Top") tail.style.bottom = `${this.floatingMenuBounds.bottom}px`;
if (this.direction === "Right") tail.style.left = `${this.floatingMenuBounds.left}px`;
if (this.direction === "Left") tail.style.right = `${this.floatingMenuBounds.right}px`;
if (!inParentFloatingMenu) {
// Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping)
const tailOffset = this.type === "Popover" ? 10 : 0;
if (this.direction === "Bottom") floatingMenuContent.style.top = `${tailOffset + this.floatingMenuBounds.top}px`;
if (this.direction === "Top") floatingMenuContent.style.bottom = `${tailOffset + this.floatingMenuBounds.bottom}px`;
if (this.direction === "Right") floatingMenuContent.style.left = `${tailOffset + this.floatingMenuBounds.left}px`;
if (this.direction === "Left") floatingMenuContent.style.right = `${tailOffset + this.floatingMenuBounds.right}px`;
// Required to correctly position content when scrolled (it has a `position: fixed` to prevent clipping)
const tail = this.$refs.tail as HTMLElement;
if (tail) {
if (this.direction === "Bottom") tail.style.top = `${this.floatingMenuBounds.top}px`;
if (this.direction === "Top") tail.style.bottom = `${this.floatingMenuBounds.bottom}px`;
if (this.direction === "Right") tail.style.left = `${this.floatingMenuBounds.left}px`;
if (this.direction === "Left") tail.style.right = `${this.floatingMenuBounds.right}px`;
}
}
type Edge = "Top" | "Bottom" | "Left" | "Right";