feat(editor): eslint/tsconfig migration + type fixes
@robonen/editor: migrate to eslint flat config + composite tsconfig; fix convergence test type annotations.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import type { AllowedComponentProps, Component, IntrinsicElementAttributes, SetupContext, VNodeProps } from 'vue';
|
||||
import { h } from 'vue';
|
||||
import { renderSlotChild } from './Slot';
|
||||
|
||||
type FunctionalComponentContext = Omit<SetupContext, 'expose'>;
|
||||
|
||||
export interface PrimitiveProps {
|
||||
as?: keyof IntrinsicElementAttributes | Component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polymorphic element renderer: renders `as` (a tag or component), or the single
|
||||
* slotted child when `as === 'template'`. Local copy of the primitives helper.
|
||||
*/
|
||||
export function Primitive(props: PrimitiveProps & VNodeProps & AllowedComponentProps & Record<string, unknown>, ctx: FunctionalComponentContext) {
|
||||
const as = props.as;
|
||||
|
||||
return as === 'template'
|
||||
? renderSlotChild(ctx.slots, ctx.attrs)
|
||||
: h(as!, ctx.attrs, ctx.slots);
|
||||
}
|
||||
|
||||
Primitive.inheritAttrs = false;
|
||||
|
||||
Primitive.props = {
|
||||
as: {
|
||||
type: [String, Object],
|
||||
default: 'div' as const,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { SetupContext, Slots, VNode } from 'vue';
|
||||
import { Comment, Fragment, cloneVNode, warn } from 'vue';
|
||||
import { getRawChildren } from './getRawChildren';
|
||||
|
||||
type FunctionalComponentContext = Omit<SetupContext, 'expose'>;
|
||||
|
||||
/**
|
||||
* Renders a single child from the provided default slot, applying attrs to it.
|
||||
* Shared between `<Slot>` and `<Primitive as="template">`.
|
||||
*
|
||||
* @param slots - Component slots
|
||||
* @param attrs - Attrs to apply to the slotted child
|
||||
* @returns Cloned VNode with merged attrs or null
|
||||
*/
|
||||
export function renderSlotChild(slots: Slots, attrs: Record<string, unknown>): VNode | null {
|
||||
if (!slots.default) return null;
|
||||
|
||||
const raw = slots.default();
|
||||
|
||||
if (raw.length === 1) {
|
||||
const only = raw[0] as VNode;
|
||||
const t = only.type;
|
||||
if (t !== Fragment && t !== Comment)
|
||||
return cloneVNode(only, attrs, true);
|
||||
}
|
||||
|
||||
const children = getRawChildren(raw);
|
||||
|
||||
if (!children.length) return null;
|
||||
|
||||
if (__DEV__ && children.length > 1) {
|
||||
warn('<Slot> can only be used on a single element or component.');
|
||||
}
|
||||
|
||||
return cloneVNode(children[0]!, attrs, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A component that renders a single child from its default slot, applying the
|
||||
* provided attributes to it.
|
||||
*
|
||||
* @param _ - Props (unused)
|
||||
* @param context - Setup context containing slots and attrs
|
||||
* @returns Cloned VNode with merged attrs or null
|
||||
*/
|
||||
export function Slot(_: Record<string, unknown>, { slots, attrs }: FunctionalComponentContext) {
|
||||
return renderSlotChild(slots, attrs);
|
||||
}
|
||||
|
||||
Slot.inheritAttrs = false;
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { VNode } from 'vue';
|
||||
import { Comment, Fragment } from 'vue';
|
||||
import { PatchFlags } from '@vue/shared';
|
||||
|
||||
/**
|
||||
* Recursively extracts and flattens VNodes from potentially nested Fragments
|
||||
* while filtering out Comment nodes. Local copy of the primitives helper to keep
|
||||
* `@robonen/editor` self-contained.
|
||||
*
|
||||
* @param children - Array of VNodes to process
|
||||
* @returns Flattened array of non-Comment VNodes
|
||||
*/
|
||||
export function getRawChildren(children: VNode[]): VNode[] {
|
||||
const result: VNode[] = [];
|
||||
flatten(children, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function flatten(children: VNode[], result: VNode[]): void {
|
||||
let keyedFragmentCount = 0;
|
||||
const startIdx = result.length;
|
||||
|
||||
for (let i = 0, len = children.length; i < len; i++) {
|
||||
const child = children[i]!;
|
||||
|
||||
if (child.type === Fragment) {
|
||||
if (child.patchFlag & PatchFlags.KEYED_FRAGMENT) {
|
||||
keyedFragmentCount++;
|
||||
}
|
||||
|
||||
flatten(child.children as VNode[], result);
|
||||
}
|
||||
else if (child.type !== Comment) {
|
||||
result.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyedFragmentCount > 1) {
|
||||
for (let i = startIdx; i < result.length; i++) {
|
||||
result[i]!.patchFlag = PatchFlags.BAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export { Primitive } from './Primitive';
|
||||
export type { PrimitiveProps } from './Primitive';
|
||||
export { Slot, renderSlotChild } from './Slot';
|
||||
export { getRawChildren } from './getRawChildren';
|
||||
Reference in New Issue
Block a user