Rename ShapeUtil.render
-> ShapeUtil.component
(#1609)
This PR renames `ShapeUtil.render` to `ShapeUtil.component`. ### Change Type - [x] `major` — Breaking change ### Release Notes - [editor] rename `ShapeUtil.render` to `ShapeUtil.component`
This commit is contained in:
parent
4dfc59e5cb
commit
3129bae6e2
22 changed files with 52 additions and 52 deletions
|
@ -48,8 +48,8 @@ export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
|
|||
}
|
||||
}
|
||||
|
||||
// Render method — the React component that will be rendered for the shape
|
||||
render(shape: CardShape) {
|
||||
// The React component that will be rendered for the shape; can return any HTML elements here
|
||||
component(shape: CardShape) {
|
||||
const bounds = this.bounds(shape)
|
||||
|
||||
return (
|
||||
|
@ -71,7 +71,7 @@ export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
|
|||
)
|
||||
}
|
||||
|
||||
// Indicator — used when hovering over a shape or when it's selected; must return only SVG elements here
|
||||
// The indicator shown when hovering over a shape or when it's selected; must return only SVG elements here
|
||||
indicator(shape: CardShape) {
|
||||
return <rect width={shape.props.w} height={shape.props.h} />
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
|
|||
}
|
||||
}
|
||||
|
||||
// Render method — the React component that will be rendered for the shape
|
||||
render(shape: CardShape) {
|
||||
// The React component that will be rendered for the shape; can return any HTML elements here
|
||||
component(shape: CardShape) {
|
||||
const bounds = this.bounds(shape)
|
||||
|
||||
return (
|
||||
|
|
|
@ -9,7 +9,7 @@ export class ErrorShapeUtil extends BaseBoxShapeUtil<ErrorShape> {
|
|||
defaultProps() {
|
||||
return { message: 'Error!', w: 100, h: 100 }
|
||||
}
|
||||
render(shape: ErrorShape) {
|
||||
component(shape: ErrorShape) {
|
||||
throw new Error(shape.props.message)
|
||||
}
|
||||
indicator() {
|
||||
|
|
|
@ -109,6 +109,8 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
// (undocumented)
|
||||
canSnap: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLArrowShape): JSX.Element | null;
|
||||
// (undocumented)
|
||||
defaultProps(): TLArrowShape['props'];
|
||||
// (undocumented)
|
||||
getArrowInfo(shape: TLArrowShape): ArrowInfo | undefined;
|
||||
|
@ -155,8 +157,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
// (undocumented)
|
||||
onTranslateStart: TLOnTranslateStartHandler<TLArrowShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLArrowShape): JSX.Element | null;
|
||||
// (undocumented)
|
||||
snapPoints(_shape: TLArrowShape): Vec2d[];
|
||||
// (undocumented)
|
||||
toSvg(shape: TLArrowShape, font: string, colors: TLExportColors): SVGGElement;
|
||||
|
@ -203,6 +203,8 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|||
// (undocumented)
|
||||
canResize: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLBookmarkShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLBookmarkShape['props'];
|
||||
// (undocumented)
|
||||
hideSelectionBoundsBg: () => boolean;
|
||||
|
@ -215,8 +217,6 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|||
// (undocumented)
|
||||
onBeforeUpdate?: TLOnBeforeUpdateHandler<TLBookmarkShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLBookmarkShape): JSX.Element;
|
||||
// (undocumented)
|
||||
static type: "bookmark";
|
||||
}
|
||||
|
||||
|
@ -312,6 +312,8 @@ export const DrawShape: TLShapeInfo<TLDrawShape>;
|
|||
|
||||
// @public (undocumented)
|
||||
export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
||||
// (undocumented)
|
||||
component(shape: TLDrawShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLDrawShape['props'];
|
||||
// (undocumented)
|
||||
|
@ -341,8 +343,6 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
|||
// (undocumented)
|
||||
onResize: TLOnResizeHandler<TLDrawShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLDrawShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLDrawShape, _font: string | undefined, colors: TLExportColors): SVGGElement;
|
||||
// (undocumented)
|
||||
static type: "draw";
|
||||
|
@ -727,6 +727,8 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
// (undocumented)
|
||||
canUnmount: TLShapeUtilFlag<TLEmbedShape>;
|
||||
// (undocumented)
|
||||
component(shape: TLEmbedShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLEmbedShape['props'];
|
||||
// (undocumented)
|
||||
hideSelectionBoundsBg: TLShapeUtilFlag<TLEmbedShape>;
|
||||
|
@ -739,8 +741,6 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
// (undocumented)
|
||||
onResize: TLOnResizeHandler<TLEmbedShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLEmbedShape): JSX.Element;
|
||||
// (undocumented)
|
||||
static type: "embed";
|
||||
}
|
||||
|
||||
|
@ -792,6 +792,8 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|||
// (undocumented)
|
||||
canReceiveNewChildrenOfType: (shape: TLShape, _type: TLShape['type']) => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLFrameShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLFrameShape['props'];
|
||||
// (undocumented)
|
||||
indicator(shape: TLFrameShape): JSX.Element;
|
||||
|
@ -806,8 +808,6 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|||
// (undocumented)
|
||||
providesBackgroundForChildren(): boolean;
|
||||
// (undocumented)
|
||||
render(shape: TLFrameShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLFrameShape, font: string, colors: TLExportColors): Promise<SVGElement> | SVGElement;
|
||||
// (undocumented)
|
||||
static type: "frame";
|
||||
|
@ -821,6 +821,8 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
// (undocumented)
|
||||
canEdit: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLGeoShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLGeoShape['props'];
|
||||
// (undocumented)
|
||||
getBounds(shape: TLGeoShape): Box2d;
|
||||
|
@ -927,8 +929,6 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
// (undocumented)
|
||||
onResize: TLOnResizeHandler<TLGeoShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLGeoShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLGeoShape, font: string, colors: TLExportColors): SVGElement;
|
||||
// (undocumented)
|
||||
static type: "geo";
|
||||
|
@ -1041,6 +1041,8 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
|
|||
// (undocumented)
|
||||
canBind: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLGroupShape): JSX.Element | null;
|
||||
// (undocumented)
|
||||
defaultProps(): TLGroupShape['props'];
|
||||
// (undocumented)
|
||||
getBounds(shape: TLGroupShape): Box2d;
|
||||
|
@ -1057,8 +1059,6 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
|
|||
// (undocumented)
|
||||
onChildrenChange: TLOnChildrenChangeHandler<TLGroupShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLGroupShape): JSX.Element | null;
|
||||
// (undocumented)
|
||||
static type: "group";
|
||||
// (undocumented)
|
||||
type: "group";
|
||||
|
@ -1083,6 +1083,8 @@ export const HighlightShape: TLShapeInfo<TLHighlightShape>;
|
|||
|
||||
// @public (undocumented)
|
||||
export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
||||
// (undocumented)
|
||||
component(shape: TLHighlightShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLHighlightShape['props'];
|
||||
// (undocumented)
|
||||
|
@ -1110,8 +1112,6 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
|||
// (undocumented)
|
||||
onResize: TLOnResizeHandler<TLHighlightShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLHighlightShape): JSX.Element;
|
||||
// (undocumented)
|
||||
renderBackground(shape: TLHighlightShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toBackgroundSvg(shape: TLHighlightShape, font: string | undefined, colors: TLExportColors): SVGPathElement;
|
||||
|
@ -1135,6 +1135,8 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
|||
// (undocumented)
|
||||
canCrop: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLImageShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLImageShape['props'];
|
||||
// (undocumented)
|
||||
indicator(shape: TLImageShape): JSX.Element | null;
|
||||
|
@ -1145,8 +1147,6 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
|||
// (undocumented)
|
||||
onDoubleClickEdge: TLOnDoubleClickHandler<TLImageShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLImageShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLImageShape): Promise<SVGGElement>;
|
||||
// (undocumented)
|
||||
static type: "image";
|
||||
|
@ -1181,6 +1181,8 @@ export const LineShape: TLShapeInfo<TLLineShape>;
|
|||
|
||||
// @public (undocumented)
|
||||
export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
||||
// (undocumented)
|
||||
component(shape: TLLineShape): JSX.Element | undefined;
|
||||
// (undocumented)
|
||||
defaultProps(): TLLineShape['props'];
|
||||
// (undocumented)
|
||||
|
@ -1214,8 +1216,6 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|||
// (undocumented)
|
||||
onResize: TLOnResizeHandler<TLLineShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLLineShape): JSX.Element | undefined;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLLineShape, _font: string, colors: TLExportColors): SVGGElement;
|
||||
// (undocumented)
|
||||
static type: "line";
|
||||
|
@ -1650,6 +1650,8 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|||
// (undocumented)
|
||||
canEdit: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLNoteShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLNoteShape['props'];
|
||||
// (undocumented)
|
||||
getBounds(shape: TLNoteShape): Box2d;
|
||||
|
@ -1716,8 +1718,6 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|||
// (undocumented)
|
||||
onEditEnd: TLOnEditEndHandler<TLNoteShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLNoteShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLNoteShape, font: string, colors: TLExportColors): SVGGElement;
|
||||
// (undocumented)
|
||||
static type: "note";
|
||||
|
@ -1833,6 +1833,7 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|||
canSnap: TLShapeUtilFlag<Shape>;
|
||||
canUnmount: TLShapeUtilFlag<Shape>;
|
||||
center(shape: Shape): Vec2d;
|
||||
abstract component(shape: Shape): any;
|
||||
abstract defaultProps(): Shape['props'];
|
||||
// (undocumented)
|
||||
editor: Editor;
|
||||
|
@ -1889,7 +1890,6 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|||
outlineSegments(shape: Shape): Vec2d[][];
|
||||
// @internal
|
||||
providesBackgroundForChildren(shape: Shape): boolean;
|
||||
abstract render(shape: Shape): any;
|
||||
// @internal
|
||||
renderBackground?(shape: Shape): any;
|
||||
// (undocumented)
|
||||
|
@ -2020,6 +2020,8 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
// (undocumented)
|
||||
canEdit: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLTextShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLTextShape['props'];
|
||||
// (undocumented)
|
||||
getBounds(shape: TLTextShape): Box2d;
|
||||
|
@ -2103,8 +2105,6 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
// (undocumented)
|
||||
onResize: TLOnResizeHandler<TLTextShape>;
|
||||
// (undocumented)
|
||||
render(shape: TLTextShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLTextShape, font: string | undefined, colors: TLExportColors): SVGGElement;
|
||||
// (undocumented)
|
||||
static type: "text";
|
||||
|
@ -2763,14 +2763,14 @@ export class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {
|
|||
// (undocumented)
|
||||
canEdit: () => boolean;
|
||||
// (undocumented)
|
||||
component(shape: TLVideoShape): JSX.Element;
|
||||
// (undocumented)
|
||||
defaultProps(): TLVideoShape['props'];
|
||||
// (undocumented)
|
||||
indicator(shape: TLVideoShape): JSX.Element;
|
||||
// (undocumented)
|
||||
isAspectRatioLocked: () => boolean;
|
||||
// (undocumented)
|
||||
render(shape: TLVideoShape): JSX.Element;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLVideoShape): SVGGElement;
|
||||
// (undocumented)
|
||||
static type: "video";
|
||||
|
|
|
@ -155,7 +155,7 @@ export const Shape = track(function Shape({
|
|||
|
||||
const InnerShape = React.memo(
|
||||
function InnerShape<T extends TLShape>({ shape, util }: { shape: T; util: ShapeUtil<T> }) {
|
||||
return useStateTracking('InnerShape:' + util.type, () => util.render(shape))
|
||||
return useStateTracking('InnerShape:' + util.type, () => util.component(shape))
|
||||
},
|
||||
(prev, next) => prev.shape.props === next.shape.props
|
||||
)
|
||||
|
|
|
@ -183,7 +183,7 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|||
* @param shape - The shape.
|
||||
* @public
|
||||
*/
|
||||
abstract render(shape: Shape): any
|
||||
abstract component(shape: Shape): any
|
||||
|
||||
/**
|
||||
* Get JSX describing the shape's indicator (as an SVG element).
|
||||
|
|
|
@ -559,7 +559,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
return false
|
||||
}
|
||||
|
||||
render(shape: TLArrowShape) {
|
||||
component(shape: TLArrowShape) {
|
||||
// Not a class component, but eslint can't tell that :(
|
||||
const onlySelectedShape = this.editor.onlySelectedShape
|
||||
const shouldDisplayHandles =
|
||||
|
|
|
@ -32,7 +32,7 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|||
}
|
||||
}
|
||||
|
||||
override render(shape: TLBookmarkShape) {
|
||||
override component(shape: TLBookmarkShape) {
|
||||
const asset = (
|
||||
shape.props.assetId ? this.editor.getAssetById(shape.props.assetId) : null
|
||||
) as TLBookmarkAsset
|
||||
|
|
|
@ -117,7 +117,7 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
|||
return false
|
||||
}
|
||||
|
||||
render(shape: TLDrawShape) {
|
||||
component(shape: TLDrawShape) {
|
||||
const forceSolid = useForceSolid()
|
||||
const strokeWidth = STROKE_SIZES[shape.props.size]
|
||||
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
||||
|
|
|
@ -72,7 +72,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
return resizeBox(shape, info, { minWidth, minHeight })
|
||||
}
|
||||
|
||||
override render(shape: TLEmbedShape) {
|
||||
override component(shape: TLEmbedShape) {
|
||||
const { w, h, url } = shape.props
|
||||
const isEditing = useIsEditing(shape.id)
|
||||
const embedInfo = useMemo(() => getEmbedInfoUnsafely(url), [url])
|
||||
|
|
|
@ -22,7 +22,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|||
return { w: 160 * 2, h: 90 * 2, name: '' }
|
||||
}
|
||||
|
||||
override render(shape: TLFrameShape) {
|
||||
override component(shape: TLFrameShape) {
|
||||
const bounds = this.bounds(shape)
|
||||
|
||||
return (
|
||||
|
|
|
@ -335,7 +335,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
}
|
||||
}
|
||||
|
||||
render(shape: TLGeoShape) {
|
||||
component(shape: TLGeoShape) {
|
||||
const { id, type, props } = shape
|
||||
|
||||
const forceSolid = useForceSolid()
|
||||
|
|
|
@ -43,7 +43,7 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
|
|||
return this.bounds(shape).corners
|
||||
}
|
||||
|
||||
render(shape: TLGroupShape) {
|
||||
component(shape: TLGroupShape) {
|
||||
// Not a class component, but eslint can't tell that :(
|
||||
const {
|
||||
erasingIdsSet,
|
||||
|
|
|
@ -92,7 +92,7 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
|||
return false
|
||||
}
|
||||
|
||||
render(shape: TLHighlightShape) {
|
||||
component(shape: TLHighlightShape) {
|
||||
return (
|
||||
<HighlightRenderer
|
||||
strokeWidth={getStrokeWidth(shape)}
|
||||
|
|
|
@ -65,7 +65,7 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
|||
}
|
||||
}
|
||||
|
||||
render(shape: TLImageShape) {
|
||||
component(shape: TLImageShape) {
|
||||
const containerStyle = getContainerStyle(shape)
|
||||
const isCropping = useIsCropping(shape.id)
|
||||
const prefersReducedMotion = usePrefersReducedMotion()
|
||||
|
|
|
@ -181,7 +181,7 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|||
return intersectLineSegmentPolyline(A, B, this.outline(shape)) !== null
|
||||
}
|
||||
|
||||
render(shape: TLLineShape) {
|
||||
component(shape: TLLineShape) {
|
||||
const forceSolid = useForceSolid()
|
||||
const spline = getSplineForLineShape(shape)
|
||||
const strokeWidth = STROKE_SIZES[shape.props.size]
|
||||
|
|
|
@ -49,7 +49,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|||
return new Vec2d(NOTE_SIZE / 2, this.getHeight(_shape) / 2)
|
||||
}
|
||||
|
||||
render(shape: TLNoteShape) {
|
||||
component(shape: TLNoteShape) {
|
||||
const {
|
||||
id,
|
||||
type,
|
||||
|
|
|
@ -78,7 +78,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
return new Vec2d(bounds.width / 2, bounds.height / 2)
|
||||
}
|
||||
|
||||
render(shape: TLTextShape) {
|
||||
component(shape: TLTextShape) {
|
||||
const {
|
||||
id,
|
||||
type,
|
||||
|
|
|
@ -27,7 +27,7 @@ export class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {
|
|||
}
|
||||
}
|
||||
|
||||
render(shape: TLVideoShape) {
|
||||
component(shape: TLVideoShape) {
|
||||
return <TLVideoUtilComponent shape={shape} videoUtil={this} />
|
||||
}
|
||||
|
||||
|
|
|
@ -458,7 +458,7 @@ describe('getShapeUtil', () => {
|
|||
defaultProps() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
render() {
|
||||
component() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
indicator() {
|
||||
|
@ -478,7 +478,7 @@ describe('getShapeUtil', () => {
|
|||
defaultProps() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
render() {
|
||||
component() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
indicator() {
|
||||
|
|
|
@ -282,7 +282,7 @@ describe('Custom shapes', () => {
|
|||
}
|
||||
}
|
||||
|
||||
render(shape: CardShape) {
|
||||
component(shape: CardShape) {
|
||||
const bounds = this.bounds(shape)
|
||||
|
||||
return (
|
||||
|
|
|
@ -28,7 +28,7 @@ class __TopLeftSnapOnlyShapeUtil extends ShapeUtil<__TopLeftSnapOnlyShape> {
|
|||
Vec2d.From({ x: shape.x, y: shape.y + shape.props.height }),
|
||||
]
|
||||
}
|
||||
render() {
|
||||
component() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
indicator() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue