mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Enable `better-tailwindcss/enforce-canonical-classes` lint rule and auto-fix all 611 violations across 173 files. Stacked on #9417. ## Changes - **What**: Simplify Tailwind classes to canonical forms via `eslint --fix`: - `h-X w-X` → `size-X` - `overflow-x-hidden overflow-y-hidden` → `overflow-hidden` - and other canonical simplifications - Enable `enforce-canonical-classes` as `'error'` in eslint config ## Review Focus Mechanical auto-fix PR — all changes produced by `eslint --fix`. No visual or behavioral changes; canonical forms are functionally identical. **Stack:** #9417 → **this PR** → PR 3 (class order) Fixes #9300 (partial — 2 of 3 rules) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9427-fix-enable-enforce-canonical-classes-tailwind-lint-rule-31a6d73d365081a49340d7d4640ede45) by [Unito](https://www.unito.io)
144 lines
4.1 KiB
Vue
144 lines
4.1 KiB
Vue
<template>
|
|
<div
|
|
class="widget-expands relative flex size-full flex-col gap-1"
|
|
@pointerdown.stop
|
|
@pointermove.stop
|
|
@pointerup.stop
|
|
>
|
|
<!-- Image preview container -->
|
|
<div
|
|
ref="containerEl"
|
|
class="relative min-h-0 flex-1 overflow-hidden rounded-[5px] bg-node-component-surface"
|
|
>
|
|
<div v-if="isLoading" class="flex size-full items-center justify-center">
|
|
<span class="text-sm">{{ $t('imageCrop.loading') }}</span>
|
|
</div>
|
|
|
|
<div
|
|
v-else-if="!imageUrl"
|
|
class="flex size-full flex-col items-center justify-center text-center"
|
|
>
|
|
<i class="mb-2 icon-[lucide--image] size-12" />
|
|
<p class="text-sm">{{ $t('imageCrop.noInputImage') }}</p>
|
|
</div>
|
|
|
|
<img
|
|
v-else
|
|
ref="imageEl"
|
|
:src="imageUrl"
|
|
:alt="$t('imageCrop.cropPreviewAlt')"
|
|
draggable="false"
|
|
class="block size-full object-contain select-none"
|
|
@load="handleImageLoad"
|
|
@error="handleImageError"
|
|
@dragstart.prevent
|
|
/>
|
|
|
|
<div
|
|
v-if="imageUrl && !isLoading"
|
|
class="absolute box-content cursor-move border-2 border-white shadow-[0_0_0_9999px_rgba(0,0,0,0.5)]"
|
|
:style="cropBoxStyle"
|
|
@pointerdown="handleDragStart"
|
|
@pointermove="handleDragMove"
|
|
@pointerup="handleDragEnd"
|
|
/>
|
|
|
|
<div
|
|
v-for="handle in resizeHandles"
|
|
v-show="imageUrl && !isLoading"
|
|
:key="handle.direction"
|
|
:class="['absolute', handle.class]"
|
|
:style="handle.style"
|
|
@pointerdown="(e) => handleResizeStart(e, handle.direction)"
|
|
@pointermove="handleResizeMove"
|
|
@pointerup="handleResizeEnd"
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex shrink-0 items-center gap-2">
|
|
<label class="text-xs text-muted-foreground">
|
|
{{ $t('imageCrop.ratio') }}
|
|
</label>
|
|
<Select v-model="selectedRatio">
|
|
<SelectTrigger class="h-7 w-24 text-xs">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem v-for="key in ratioKeys" :key="key" :value="key">
|
|
{{ key === 'custom' ? $t('imageCrop.custom') : key }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<Button
|
|
size="icon"
|
|
:variant="isLockEnabled ? 'primary' : 'secondary'"
|
|
class="size-7"
|
|
:aria-label="
|
|
isLockEnabled
|
|
? $t('imageCrop.unlockRatio')
|
|
: $t('imageCrop.lockRatio')
|
|
"
|
|
@click="isLockEnabled = !isLockEnabled"
|
|
>
|
|
<i
|
|
:class="
|
|
isLockEnabled
|
|
? 'icon-[lucide--lock] size-3.5'
|
|
: 'icon-[lucide--lock-open] size-3.5'
|
|
"
|
|
/>
|
|
</Button>
|
|
</div>
|
|
|
|
<WidgetBoundingBox v-model="modelValue" class="shrink-0" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useTemplateRef } from 'vue'
|
|
|
|
import WidgetBoundingBox from '@/components/boundingbox/WidgetBoundingBox.vue'
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import Select from '@/components/ui/select/Select.vue'
|
|
import SelectContent from '@/components/ui/select/SelectContent.vue'
|
|
import SelectItem from '@/components/ui/select/SelectItem.vue'
|
|
import SelectTrigger from '@/components/ui/select/SelectTrigger.vue'
|
|
import SelectValue from '@/components/ui/select/SelectValue.vue'
|
|
import { ASPECT_RATIOS, useImageCrop } from '@/composables/useImageCrop'
|
|
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
|
|
import type { Bounds } from '@/renderer/core/layout/types'
|
|
|
|
const props = defineProps<{
|
|
nodeId: NodeId
|
|
}>()
|
|
|
|
const modelValue = defineModel<Bounds>({
|
|
default: () => ({ x: 0, y: 0, width: 512, height: 512 })
|
|
})
|
|
|
|
const imageEl = useTemplateRef<HTMLImageElement>('imageEl')
|
|
const containerEl = useTemplateRef<HTMLDivElement>('containerEl')
|
|
|
|
const ratioKeys = Object.keys(ASPECT_RATIOS)
|
|
|
|
const {
|
|
imageUrl,
|
|
isLoading,
|
|
|
|
selectedRatio,
|
|
isLockEnabled,
|
|
|
|
cropBoxStyle,
|
|
resizeHandles,
|
|
|
|
handleImageLoad,
|
|
handleImageError,
|
|
handleDragStart,
|
|
handleDragMove,
|
|
handleDragEnd,
|
|
handleResizeStart,
|
|
handleResizeMove,
|
|
handleResizeEnd
|
|
} = useImageCrop(props.nodeId, { imageEl, containerEl, modelValue })
|
|
</script>
|