mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
feat(editor): Make workflows, credentials, executions and new canvas usable on mobile and touch devices (#12372)
This commit is contained in:
parent
1b9100032f
commit
06c9473210
|
@ -34,7 +34,11 @@ const classes = computed(() => ({
|
|||
<slot name="footer" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$slots.append" data-test-id="card-append" :class="$style.append">
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
data-test-id="card-append"
|
||||
:class="[$style.append, 'n8n-card-append']"
|
||||
>
|
||||
<slot name="append" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,7 +49,7 @@ const classes = computed(() => ({
|
|||
border-radius: var(--border-radius-large);
|
||||
border: var(--border-base);
|
||||
background-color: var(--color-background-xlight);
|
||||
padding: var(--spacing-s);
|
||||
padding: var(--card--padding, var(--spacing-s));
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
|
@ -101,5 +105,6 @@ const classes = computed(() => ({
|
|||
display: flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
width: var(--card--append--width, unset);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -75,6 +75,38 @@ describe('useDeviceSupport()', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('isMobileDevice', () => {
|
||||
it('should be true for iOS user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'iphone' });
|
||||
const { isMobileDevice } = useDeviceSupport();
|
||||
expect(isMobileDevice).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be true for Android user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'android' });
|
||||
const { isMobileDevice } = useDeviceSupport();
|
||||
expect(isMobileDevice).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be false for non-mobile user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'windows' });
|
||||
const { isMobileDevice } = useDeviceSupport();
|
||||
expect(isMobileDevice).toEqual(false);
|
||||
});
|
||||
|
||||
it('should be true for iPad user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'ipad' });
|
||||
const { isMobileDevice } = useDeviceSupport();
|
||||
expect(isMobileDevice).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be true for iPod user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'ipod' });
|
||||
const { isMobileDevice } = useDeviceSupport();
|
||||
expect(isMobileDevice).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isCtrlKeyPressed()', () => {
|
||||
it('should return true for metaKey press on macOS', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'macintosh' });
|
||||
|
|
|
@ -12,12 +12,16 @@ export function useDeviceSupport() {
|
|||
!window.matchMedia('(any-pointer: fine)').matches,
|
||||
);
|
||||
const userAgent = ref(navigator.userAgent.toLowerCase());
|
||||
const isMacOs = ref(
|
||||
userAgent.value.includes('macintosh') ||
|
||||
|
||||
const isIOs = ref(
|
||||
userAgent.value.includes('iphone') ||
|
||||
userAgent.value.includes('ipad') ||
|
||||
userAgent.value.includes('iphone') ||
|
||||
userAgent.value.includes('ipod'),
|
||||
);
|
||||
const isAndroidOs = ref(userAgent.value.includes('android'));
|
||||
const isMacOs = ref(userAgent.value.includes('macintosh') || isIOs.value);
|
||||
const isMobileDevice = ref(isIOs.value || isAndroidOs.value);
|
||||
|
||||
const controlKeyCode = ref(isMacOs.value ? 'Meta' : 'Control');
|
||||
|
||||
function isCtrlKeyPressed(e: MouseEvent | KeyboardEvent): boolean {
|
||||
|
@ -30,7 +34,10 @@ export function useDeviceSupport() {
|
|||
return {
|
||||
userAgent: userAgent.value,
|
||||
isTouchDevice: isTouchDevice.value,
|
||||
isAndroidOs: isAndroidOs.value,
|
||||
isIOs: isIOs.value,
|
||||
isMacOs: isMacOs.value,
|
||||
isMobileDevice: isMobileDevice.value,
|
||||
controlKeyCode: controlKeyCode.value,
|
||||
isCtrlKeyPressed,
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
@use './base.scss';
|
||||
@use './pagination.scss';
|
||||
@use './dialog.scss';
|
||||
@use './display.scss';
|
||||
// @use "./autocomplete.scss";
|
||||
@use './dropdown.scss';
|
||||
@use './dropdown-menu.scss';
|
||||
|
|
20
packages/design-system/src/css/mixins/_breakpoints.scss
Normal file
20
packages/design-system/src/css/mixins/_breakpoints.scss
Normal file
|
@ -0,0 +1,20 @@
|
|||
@use '../common/var';
|
||||
|
||||
@mixin breakpoint($name) {
|
||||
@if map-has-key(var.$breakpoints-spec, $name) {
|
||||
$query: map-get(var.$breakpoints-spec, $name);
|
||||
$media-query: '';
|
||||
|
||||
@each $key, $value in $query {
|
||||
$media-query: '#{$media-query} and (#{$key}: #{$value})';
|
||||
}
|
||||
|
||||
$media-query: unquote(str-slice($media-query, 6)); // Remove the initial ' and '
|
||||
|
||||
@media screen and #{$media-query} {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@error "No breakpoint named `#{$name}` found in `$breakpoints-spec`.";
|
||||
}
|
||||
}
|
6
packages/design-system/src/css/mixins/index.scss
Normal file
6
packages/design-system/src/css/mixins/index.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
@forward 'breakpoints';
|
||||
@forward 'button';
|
||||
@forward 'config';
|
||||
@forward 'function';
|
||||
@forward 'mixins';
|
||||
@forward 'utils';
|
|
@ -192,6 +192,8 @@ watch(defaultLocale, (newLocale) => {
|
|||
.header {
|
||||
grid-area: header;
|
||||
z-index: var(--z-index-app-header);
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
|
|
@ -162,6 +162,7 @@ function moveResource() {
|
|||
<template #append>
|
||||
<div :class="$style.cardActions" @click.stop>
|
||||
<ProjectCardBadge
|
||||
:class="$style.cardBadge"
|
||||
:resource="data"
|
||||
:resource-type="ResourceType.Credential"
|
||||
:resource-type-label="resourceTypeLabel"
|
||||
|
@ -180,9 +181,10 @@ function moveResource() {
|
|||
|
||||
<style lang="scss" module>
|
||||
.cardLink {
|
||||
--card--padding: 0 0 0 var(--spacing-s);
|
||||
|
||||
transition: box-shadow 0.3s ease;
|
||||
cursor: pointer;
|
||||
padding: 0 0 0 var(--spacing-s);
|
||||
align-items: stretch;
|
||||
|
||||
&:hover {
|
||||
|
@ -215,4 +217,22 @@ function moveResource() {
|
|||
padding: 0 var(--spacing-s) 0 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@include mixins.breakpoint('sm-and-down') {
|
||||
.cardLink {
|
||||
--card--padding: 0 var(--spacing-s) var(--spacing-s);
|
||||
--card--append--width: 100%;
|
||||
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cardActions {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cardBadge {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -220,7 +220,7 @@ function hideGithubButton() {
|
|||
@update:model-value="onTabSelected"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showGitHubButton" class="github-button">
|
||||
<div v-if="showGitHubButton" class="github-button hidden-sm-and-down">
|
||||
<div class="github-button-container">
|
||||
<GithubButton
|
||||
href="https://github.com/n8n-io/n8n"
|
||||
|
@ -264,6 +264,7 @@ function hideGithubButton() {
|
|||
font-size: 0.9em;
|
||||
font-weight: 400;
|
||||
padding: var(--spacing-xs) var(--spacing-m);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.github-button {
|
||||
|
|
|
@ -800,6 +800,8 @@ $--header-spacing: 20px;
|
|||
.name {
|
||||
color: $custom-font-dark;
|
||||
font-size: 15px;
|
||||
display: block;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.activator {
|
||||
|
@ -807,7 +809,6 @@ $--header-spacing: 20px;
|
|||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: $--text-line-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> span {
|
||||
|
@ -845,24 +846,24 @@ $--header-spacing: 20px;
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-m);
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style module lang="scss">
|
||||
.container {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.hiddenInput {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useCredentialsStore } from '@/stores/credentials.store';
|
|||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { DRAG_EVENT_DATA_KEY } from '@/constants';
|
||||
import { useAssistantStore } from '@/stores/assistant.store';
|
||||
import N8nIconButton from 'n8n-design-system/components/N8nIconButton/IconButton.vue';
|
||||
|
||||
export interface Props {
|
||||
active?: boolean;
|
||||
|
@ -145,6 +146,14 @@ onBeforeUnmount(() => {
|
|||
[$style.active]: showScrim,
|
||||
}"
|
||||
/>
|
||||
<N8nIconButton
|
||||
v-if="active"
|
||||
:class="$style.close"
|
||||
type="secondary"
|
||||
icon="times"
|
||||
aria-label="Close Node Creator"
|
||||
@click="emit('closeNodeCreator')"
|
||||
/>
|
||||
<SlideTransition>
|
||||
<div
|
||||
v-if="active"
|
||||
|
@ -168,13 +177,14 @@ onBeforeUnmount(() => {
|
|||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
.nodeCreator {
|
||||
--node-creator-width: #{$node-creator-width};
|
||||
--node-icon-color: var(--color-text-base);
|
||||
position: fixed;
|
||||
top: $header-height;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: var(--z-index-node-creator);
|
||||
width: $node-creator-width;
|
||||
width: var(--node-creator-width);
|
||||
color: $node-creator-text-color;
|
||||
}
|
||||
|
||||
|
@ -194,4 +204,24 @@ onBeforeUnmount(() => {
|
|||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
z-index: calc(var(--z-index-node-creator) + 1);
|
||||
top: var(--spacing-xs);
|
||||
right: var(--spacing-xs);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: #{$node-creator-width + $sidebar-width}) {
|
||||
.nodeCreator {
|
||||
--node-creator-width: calc(100vw - #{$sidebar-width});
|
||||
}
|
||||
|
||||
.close {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -260,7 +260,7 @@ function onBackButton() {
|
|||
height: 100%;
|
||||
background-color: $node-creator-background-color;
|
||||
--color-background-node-icon-badge: var(--color-background-xlight);
|
||||
width: 385px;
|
||||
width: var(--node-creator-width);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
@ -303,6 +303,7 @@ function onBackButton() {
|
|||
line-height: 24px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
font-size: var(--font-size-l);
|
||||
margin: 0;
|
||||
|
||||
.hasBg & {
|
||||
font-size: var(--font-size-s-m);
|
||||
|
|
|
@ -126,7 +126,7 @@ const badgeTooltip = computed(() => {
|
|||
</script>
|
||||
<template>
|
||||
<N8nTooltip :disabled="!badgeTooltip" placement="top">
|
||||
<div class="mr-xs">
|
||||
<div :class="$style.wrapper" v-bind="$attrs">
|
||||
<N8nBadge
|
||||
v-if="badgeText"
|
||||
:class="$style.badge"
|
||||
|
@ -153,6 +153,10 @@ const badgeTooltip = computed(() => {
|
|||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.wrapper {
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: var(--spacing-4xs) var(--spacing-2xs);
|
||||
background-color: var(--color-background-xlight);
|
||||
|
|
|
@ -106,10 +106,10 @@ const onSelect = (action: string) => {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div :class="[$style.projectHeader]">
|
||||
<div :class="[$style.projectDetails]">
|
||||
<div :class="$style.projectHeader">
|
||||
<div :class="$style.projectDetails">
|
||||
<ProjectIcon :icon="headerIcon" :border-less="true" size="medium" />
|
||||
<div>
|
||||
<div :class="$style.headerActions">
|
||||
<N8nHeading bold tag="h2" size="xlarge">{{ projectName }}</N8nHeading>
|
||||
<N8nText color="text-light">
|
||||
<slot name="subtitle">
|
||||
|
@ -147,7 +147,8 @@ const onSelect = (action: string) => {
|
|||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.projectHeader {
|
||||
.projectHeader,
|
||||
.projectDescription {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
@ -163,4 +164,16 @@ const onSelect = (action: string) => {
|
|||
.actions {
|
||||
padding: var(--spacing-2xs) 0 var(--spacing-l);
|
||||
}
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
.projectHeader {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.headerActions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -268,6 +268,7 @@ function moveResource() {
|
|||
<template #append>
|
||||
<div :class="$style.cardActions" @click.stop>
|
||||
<ProjectCardBadge
|
||||
:class="$style.cardBadge"
|
||||
:resource="data"
|
||||
:resource-type="ResourceType.Workflow"
|
||||
:resource-type-label="resourceTypeLabel"
|
||||
|
@ -330,4 +331,22 @@ function moveResource() {
|
|||
padding: 0 var(--spacing-s) 0 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@include mixins.breakpoint('sm-and-down') {
|
||||
.cardLink {
|
||||
--card--padding: 0 var(--spacing-s) var(--spacing-s);
|
||||
--card--append--width: 100%;
|
||||
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cardActions {
|
||||
width: 100%;
|
||||
padding: 0 var(--spacing-s) var(--spacing-s);
|
||||
}
|
||||
|
||||
.cardBadge {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -93,7 +93,7 @@ const props = withDefaults(
|
|||
},
|
||||
);
|
||||
|
||||
const { controlKeyCode } = useDeviceSupport();
|
||||
const { isMobileDevice, controlKeyCode } = useDeviceSupport();
|
||||
|
||||
const vueFlow = useVueFlow({ id: props.id, deleteKeyCode: null });
|
||||
const {
|
||||
|
@ -143,9 +143,10 @@ const disableKeyBindings = computed(() => !props.keyBindings);
|
|||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#whitespace_keys
|
||||
*/
|
||||
const panningKeyCode = ref<string[]>([' ', controlKeyCode]);
|
||||
const panningMouseButton = ref<number[]>([1]);
|
||||
const selectionKeyCode = ref<true | null>(true);
|
||||
|
||||
const panningKeyCode = ref<string[] | true>(isMobileDevice ? true : [' ', controlKeyCode]);
|
||||
const panningMouseButton = ref<number[] | true>(isMobileDevice ? true : [1]);
|
||||
const selectionKeyCode = ref<string | true | null>(isMobileDevice ? 'Shift' : true);
|
||||
|
||||
onKeyDown(panningKeyCode.value, () => {
|
||||
selectionKeyCode.value = null;
|
||||
|
|
|
@ -483,6 +483,10 @@ const goToUpgrade = () => {
|
|||
width: 100%;
|
||||
padding: var(--spacing-l) var(--spacing-2xl) 0;
|
||||
max-width: var(--content-container-width);
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
padding: var(--spacing-xs) var(--spacing-xs) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.execList {
|
||||
|
|
|
@ -129,4 +129,14 @@ onBeforeRouteLeave(async (to, _, next) => {
|
|||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@include mixins.breakpoint('sm-and-down') {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -265,6 +265,7 @@ const goToUpgrade = () => {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.heading {
|
||||
|
@ -314,9 +315,10 @@ const goToUpgrade = () => {
|
|||
bottom: 0;
|
||||
margin-left: calc(-1 * var(--spacing-l));
|
||||
border-top: var(--border-base);
|
||||
width: 100%;
|
||||
|
||||
& > div {
|
||||
width: 309px;
|
||||
width: 100%;
|
||||
background-color: var(--color-background-light);
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
|
|
@ -99,10 +99,16 @@ onBeforeMount(async () => {
|
|||
:class="$style['filter-button']"
|
||||
data-test-id="resources-list-filters-trigger"
|
||||
>
|
||||
<n8n-badge v-show="filtersLength > 0" theme="primary" class="mr-4xs">
|
||||
<n8n-badge
|
||||
v-show="filtersLength > 0"
|
||||
:class="$style['filter-button-count']"
|
||||
theme="primary"
|
||||
>
|
||||
{{ filtersLength }}
|
||||
</n8n-badge>
|
||||
{{ i18n.baseText('forms.resourceFiltersDropdown.filters') }}
|
||||
<span :class="$style['filter-button-text']">
|
||||
{{ i18n.baseText('forms.resourceFiltersDropdown.filters') }}
|
||||
</span>
|
||||
</n8n-button>
|
||||
</template>
|
||||
<div :class="$style['filters-dropdown']" data-test-id="resources-list-filters-dropdown">
|
||||
|
@ -139,6 +145,25 @@ onBeforeMount(async () => {
|
|||
.filter-button {
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
|
||||
.filter-button-count {
|
||||
margin-right: var(--spacing-4xs);
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.filter-button-text {
|
||||
text-indent: -10000px;
|
||||
}
|
||||
|
||||
// Remove icon margin when the "Filters" text is hidden
|
||||
:global(span + span) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filters-dropdown {
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
box-sizing: border-box;
|
||||
align-content: start;
|
||||
padding: var(--spacing-l) var(--spacing-2xl) 0;
|
||||
|
||||
@include mixins.breakpoint('sm-and-down') {
|
||||
padding: var(--spacing-s) var(--spacing-s) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
|
|
@ -475,6 +475,7 @@ onMounted(async () => {
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filters {
|
||||
|
@ -483,10 +484,24 @@ onMounted(async () => {
|
|||
grid-auto-columns: max-content;
|
||||
gap: var(--spacing-2xs);
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-auto-flow: row;
|
||||
|
||||
> *:last-child {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
max-width: 240px;
|
||||
|
||||
@include mixins.breakpoint('sm-and-down') {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.listWrapper {
|
||||
|
@ -497,6 +512,10 @@ onMounted(async () => {
|
|||
|
||||
.sort-and-filter {
|
||||
white-space: nowrap;
|
||||
|
||||
@include mixins.breakpoint('sm-and-down') {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.datatable {
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
fill: var(--color-foreground-dark);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,3 +104,20 @@
|
|||
.vue-flow__edge-label.selected {
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls
|
||||
*/
|
||||
|
||||
.vue-flow__controls {
|
||||
margin: var(--spacing-s);
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
max-width: calc(100% - 3 * var(--spacing-s) - var(--spacing-2xs));
|
||||
overflow: auto;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: var(--spacing-s);
|
||||
padding-right: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1772,11 +1772,13 @@ onBeforeUnmount(() => {
|
|||
align-items: center;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: var(--spacing-l);
|
||||
bottom: var(--spacing-s);
|
||||
width: auto;
|
||||
|
||||
@media (max-width: $breakpoint-2xs) {
|
||||
bottom: 150px;
|
||||
@include mixins.breakpoint('sm-only') {
|
||||
left: auto;
|
||||
right: var(--spacing-s);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
button {
|
||||
|
@ -1788,6 +1790,17 @@ onBeforeUnmount(() => {
|
|||
&:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
text-indent: -10000px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
padding: 0;
|
||||
|
||||
span {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,11 @@ export default mergeConfig(
|
|||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '\n@use "@/n8n-theme-variables.scss" as *;\n',
|
||||
additionalData: [
|
||||
'',
|
||||
'@use "@/n8n-theme-variables.scss" as *;',
|
||||
'@use "n8n-design-system/css/mixins" as mixins;',
|
||||
].join('\n'),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue