mirror of https://github.com/misskey-dev/misskey
* perf(#10923): unwind css module class name
* perf(#10923): support multiple components
* refactor: clean up
* refactor(#10923): avoid `useCssModule()`
* fix(#10923): allow direct literal class name
* fix(#10923): avoid computed class name
* fix(#10923): allow literal keys
* fix(#10923): typo
* fix(#10923): invalid class names
* chore: test
* revert: test
This reverts commit 5c7ef366ec
.
* fix(#10923): hidden tale
* perf(#10923): also unwind scoped css contained components
* perf(#10923): `normalizeClass` AOT compilation
---------
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
pull/10939/head
parent
146e89edd0
commit
337dd97b49
|
@ -0,0 +1,597 @@
|
|||
import { parse } from 'acorn';
|
||||
import { generate } from 'astring';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name';
|
||||
import type * as estree from 'estree';
|
||||
|
||||
function parseExpression(code: string): estree.Expression {
|
||||
const program = parse(code, { ecmaVersion: 'latest', sourceType: 'module' }) as unknown as estree.Program;
|
||||
const statement = program.body[0] as estree.ExpressionStatement;
|
||||
return statement.expression;
|
||||
}
|
||||
|
||||
describe(normalizeClass.name, () => {
|
||||
it('should normalize string', () => {
|
||||
expect(normalizeClass(parseExpression('"a b c"'))).toBe('a b c');
|
||||
});
|
||||
it('should trim redundant spaces', () => {
|
||||
expect(normalizeClass(parseExpression('" a b c "'))).toBe('a b c');
|
||||
});
|
||||
it('should ignore undefined', () => {
|
||||
expect(normalizeClass(parseExpression('undefined'))).toBe('');
|
||||
});
|
||||
it('should ignore non string literals', () => {
|
||||
expect(normalizeClass(parseExpression('0'))).toBe('');
|
||||
expect(normalizeClass(parseExpression('true'))).toBe('');
|
||||
expect(normalizeClass(parseExpression('null'))).toBe('');
|
||||
expect(normalizeClass(parseExpression('/I.D/'))).toBe('');
|
||||
});
|
||||
it('should not normalize identifiers', () => {
|
||||
expect(normalizeClass(parseExpression('EScape'))).toBeNull();
|
||||
});
|
||||
it('should normalize recursively array', () => {
|
||||
expect(normalizeClass(parseExpression('["from", ...["Utopia"]]'))).toBe('from Utopia');
|
||||
expect(normalizeClass(parseExpression('["from", ...[Utopia]]'))).toBeNull();
|
||||
});
|
||||
it('should normalize recursively template literal', () => {
|
||||
expect(normalizeClass(parseExpression('`name ${"shiho"} code ${33}`'))).toBe('name shiho code');
|
||||
expect(normalizeClass(parseExpression('`name ${shiho.name} code ${33}`'))).toBeNull();
|
||||
});
|
||||
it('should normalize recursively binary expression', () => {
|
||||
expect(normalizeClass(parseExpression('"mirage" + "mirror"'))).toBe('miragemirror');
|
||||
expect(normalizeClass(parseExpression('"mirage" + mirror'))).toBeNull();
|
||||
});
|
||||
it('should normalize recursively object expression', () => {
|
||||
expect(normalizeClass(parseExpression('({ a: true, b: "c" })'))).toBe('a b');
|
||||
expect(normalizeClass(parseExpression('({ a: false, b: "c" })'))).toBe('b');
|
||||
expect(normalizeClass(parseExpression('({ a: true, b: c })'))).toBeNull();
|
||||
expect(normalizeClass(parseExpression('({ a: true, b: "c", ...({ d: true }) })'))).toBe('a b d');
|
||||
expect(normalizeClass(parseExpression('({ a: true, [b]: "c" })'))).toBeNull();
|
||||
expect(normalizeClass(parseExpression('({ a: true, b: false, c: !false, d: !!0 })'))).toBe('a c');
|
||||
});
|
||||
});
|
||||
|
||||
it('Composition API (standard)', () => {
|
||||
const ast = parse(`
|
||||
import { c as api, d as defaultStore, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js';
|
||||
import { M as MkContainer } from './MkContainer-!~{03M}~.js';
|
||||
import { b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode } from './vue-!~{002}~.js';
|
||||
import './photoswipe-!~{003}~.js';
|
||||
|
||||
const _hoisted_1 = /* @__PURE__ */ createBaseVNode("i", { class: "ti ti-photo" }, null, -1);
|
||||
const _sfc_main = /* @__PURE__ */ defineComponent({
|
||||
__name: "index.photos",
|
||||
props: {
|
||||
user: {}
|
||||
},
|
||||
setup(__props) {
|
||||
const props = __props;
|
||||
let fetching = ref(true);
|
||||
let images = ref([]);
|
||||
function thumbnail(image) {
|
||||
return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
|
||||
}
|
||||
onMounted(() => {
|
||||
const image = [
|
||||
"image/jpeg",
|
||||
"image/webp",
|
||||
"image/avif",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/apng",
|
||||
"image/vnd.mozilla.apng"
|
||||
];
|
||||
api("users/notes", {
|
||||
userId: props.user.id,
|
||||
fileType: image,
|
||||
excludeNsfw: defaultStore.state.nsfw !== "ignore",
|
||||
limit: 10
|
||||
}).then((notes) => {
|
||||
for (const note of notes) {
|
||||
for (const file of note.files) {
|
||||
images.value.push({
|
||||
note,
|
||||
file
|
||||
});
|
||||
}
|
||||
}
|
||||
fetching.value = false;
|
||||
});
|
||||
});
|
||||
return (_ctx, _cache) => {
|
||||
const _component_MkLoading = resolveComponent("MkLoading");
|
||||
const _component_MkA = resolveComponent("MkA");
|
||||
return openBlock(), createBlock(MkContainer, {
|
||||
"max-height": 300,
|
||||
foldable: true
|
||||
}, {
|
||||
icon: withCtx(() => [
|
||||
_hoisted_1
|
||||
]),
|
||||
header: withCtx(() => [
|
||||
createTextVNode(toDisplayString(unref(i18n).ts.images), 1)
|
||||
]),
|
||||
default: withCtx(() => [
|
||||
createBaseVNode("div", {
|
||||
class: normalizeClass(_ctx.$style.root)
|
||||
}, [
|
||||
unref(fetching) ? (openBlock(), createBlock(_component_MkLoading, { key: 0 })) : createCommentVNode("", true),
|
||||
!unref(fetching) && unref(images).length > 0 ? (openBlock(), createElementBlock("div", {
|
||||
key: 1,
|
||||
class: normalizeClass(_ctx.$style.stream)
|
||||
}, [
|
||||
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(images), (image) => {
|
||||
return openBlock(), createBlock(_component_MkA, {
|
||||
key: image.note.id + image.file.id,
|
||||
class: normalizeClass(_ctx.$style.img),
|
||||
to: unref(notePage)(image.note)
|
||||
}, {
|
||||
default: withCtx(() => [
|
||||
createVNode(ImgWithBlurhash, {
|
||||
hash: image.file.blurhash,
|
||||
src: thumbnail(image.file),
|
||||
title: image.file.name
|
||||
}, null, 8, ["hash", "src", "title"])
|
||||
]),
|
||||
_: 2
|
||||
}, 1032, ["class", "to"]);
|
||||
}), 128))
|
||||
], 2)) : createCommentVNode("", true),
|
||||
!unref(fetching) && unref(images).length == 0 ? (openBlock(), createElementBlock("p", {
|
||||
key: 2,
|
||||
class: normalizeClass(_ctx.$style.empty)
|
||||
}, toDisplayString(unref(i18n).ts.nothing), 3)) : createCommentVNode("", true)
|
||||
], 2)
|
||||
]),
|
||||
_: 1
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const root = "xenMW";
|
||||
const stream = "xaZzf";
|
||||
const img = "xtA8t";
|
||||
const empty = "xhYKj";
|
||||
const style0 = {
|
||||
root: root,
|
||||
stream: stream,
|
||||
img: img,
|
||||
empty: empty
|
||||
};
|
||||
|
||||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const index_photos = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
|
||||
|
||||
export { index_photos as default };
|
||||
`.slice(1), { ecmaVersion: 'latest', sourceType: 'module' });
|
||||
unwindCssModuleClassName(ast);
|
||||
expect(generate(ast)).toBe(`
|
||||
import {c as api, d as defaultStore, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc} from './app-!~{001}~.js';
|
||||
import {M as MkContainer} from './MkContainer-!~{03M}~.js';
|
||||
import {b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode} from './vue-!~{002}~.js';
|
||||
import './photoswipe-!~{003}~.js';
|
||||
const _hoisted_1 = createBaseVNode("i", {
|
||||
class: "ti ti-photo"
|
||||
}, null, -1);
|
||||
const _sfc_main = defineComponent({
|
||||
__name: "index.photos",
|
||||
props: {
|
||||
user: {}
|
||||
},
|
||||
setup(__props) {
|
||||
const props = __props;
|
||||
let fetching = ref(true);
|
||||
let images = ref([]);
|
||||
function thumbnail(image) {
|
||||
return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
|
||||
}
|
||||
onMounted(() => {
|
||||
const image = ["image/jpeg", "image/webp", "image/avif", "image/png", "image/gif", "image/apng", "image/vnd.mozilla.apng"];
|
||||
api("users/notes", {
|
||||
userId: props.user.id,
|
||||
fileType: image,
|
||||
excludeNsfw: defaultStore.state.nsfw !== "ignore",
|
||||
limit: 10
|
||||
}).then(notes => {
|
||||
for (const note of notes) {
|
||||
for (const file of note.files) {
|
||||
images.value.push({
|
||||
note,
|
||||
file
|
||||
});
|
||||
}
|
||||
}
|
||||
fetching.value = false;
|
||||
});
|
||||
});
|
||||
return (_ctx, _cache) => {
|
||||
const _component_MkLoading = resolveComponent("MkLoading");
|
||||
const _component_MkA = resolveComponent("MkA");
|
||||
return (openBlock(), createBlock(MkContainer, {
|
||||
"max-height": 300,
|
||||
foldable: true
|
||||
}, {
|
||||
icon: withCtx(() => [_hoisted_1]),
|
||||
header: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).ts.images), 1)]),
|
||||
default: withCtx(() => [createBaseVNode("div", {
|
||||
class: "xenMW"
|
||||
}, [unref(fetching) ? (openBlock(), createBlock(_component_MkLoading, {
|
||||
key: 0
|
||||
})) : createCommentVNode("", true), !unref(fetching) && unref(images).length > 0 ? (openBlock(), createElementBlock("div", {
|
||||
key: 1,
|
||||
class: "xaZzf"
|
||||
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(images), image => {
|
||||
return (openBlock(), createBlock(_component_MkA, {
|
||||
key: image.note.id + image.file.id,
|
||||
class: "xtA8t",
|
||||
to: unref(notePage)(image.note)
|
||||
}, {
|
||||
default: withCtx(() => [createVNode(ImgWithBlurhash, {
|
||||
hash: image.file.blurhash,
|
||||
src: thumbnail(image.file),
|
||||
title: image.file.name
|
||||
}, null, 8, ["hash", "src", "title"])]),
|
||||
_: 2
|
||||
}, 1032, ["class", "to"]));
|
||||
}), 128))], 2)) : createCommentVNode("", true), !unref(fetching) && unref(images).length == 0 ? (openBlock(), createElementBlock("p", {
|
||||
key: 2,
|
||||
class: "xhYKj"
|
||||
}, toDisplayString(unref(i18n).ts.nothing), 3)) : createCommentVNode("", true)], 2)]),
|
||||
_: 1
|
||||
}));
|
||||
};
|
||||
}
|
||||
});
|
||||
const root = "xenMW";
|
||||
const stream = "xaZzf";
|
||||
const img = "xtA8t";
|
||||
const empty = "xhYKj";
|
||||
const style0 = {
|
||||
root: root,
|
||||
stream: stream,
|
||||
img: img,
|
||||
empty: empty
|
||||
};
|
||||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const index_photos = _sfc_main;
|
||||
export {index_photos as default};
|
||||
`.slice(1));
|
||||
});
|
||||
|
||||
it('Composition API (with `useCssModule()`)', () => {
|
||||
const ast = parse(`
|
||||
import { a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup } from './!~{002}~.js';
|
||||
import { d as defaultStore, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js';
|
||||
|
||||
function isDebuggerEnabled(id) {
|
||||
try {
|
||||
return localStorage.getItem(\`DEBUG_\${id}\`) !== null;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function stackTraceInstances() {
|
||||
let instance = getCurrentInstance();
|
||||
const stack = [];
|
||||
while (instance) {
|
||||
stack.push(instance);
|
||||
instance = instance.parent;
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
const _sfc_main = defineComponent({
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "down"
|
||||
},
|
||||
reversed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
noGap: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
ad: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { slots, expose }) {
|
||||
const $style = useCssModule();
|
||||
function getDateText(time) {
|
||||
const date = new Date(time).getDate();
|
||||
const month = new Date(time).getMonth() + 1;
|
||||
return i18n.t("monthAndDay", {
|
||||
month: month.toString(),
|
||||
day: date.toString()
|
||||
});
|
||||
}
|
||||
if (props.items.length === 0)
|
||||
return;
|
||||
const renderChildrenImpl = () => props.items.map((item, i) => {
|
||||
if (!slots || !slots.default)
|
||||
return;
|
||||
const el = slots.default({
|
||||
item
|
||||
})[0];
|
||||
if (el.key == null && item.id)
|
||||
el.key = item.id;
|
||||
if (i !== props.items.length - 1 && new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate()) {
|
||||
const separator = h("div", {
|
||||
class: $style["separator"],
|
||||
key: item.id + ":separator"
|
||||
}, h("p", {
|
||||
class: $style["date"]
|
||||
}, [
|
||||
h("span", {
|
||||
class: $style["date-1"]
|
||||
}, [
|
||||
h("i", {
|
||||
class: \`ti ti-chevron-up \${$style["date-1-icon"]}\`
|
||||
}),
|
||||
getDateText(item.createdAt)
|
||||
]),
|
||||
h("span", {
|
||||
class: $style["date-2"]
|
||||
}, [
|
||||
getDateText(props.items[i + 1].createdAt),
|
||||
h("i", {
|
||||
class: \`ti ti-chevron-down \${$style["date-2-icon"]}\`
|
||||
})
|
||||
])
|
||||
]));
|
||||
return [el, separator];
|
||||
} else {
|
||||
if (props.ad && item._shouldInsertAd_) {
|
||||
return [h(MkAd, {
|
||||
key: item.id + ":ad",
|
||||
prefer: ["horizontal", "horizontal-big"]
|
||||
}), el];
|
||||
} else {
|
||||
return el;
|
||||
}
|
||||
}
|
||||
});
|
||||
const renderChildren = () => {
|
||||
const children = renderChildrenImpl();
|
||||
if (isDebuggerEnabled(6864)) {
|
||||
const nodes = children.flatMap((node) => node ?? []);
|
||||
const keys = new Set(nodes.map((node) => node.key));
|
||||
if (keys.size !== nodes.length) {
|
||||
const id = crypto.randomUUID();
|
||||
const instances = stackTraceInstances();
|
||||
toast(instances.reduce((a, c) => \`\${a} at \${c.type.name}\`, \`[DEBUG_6864 (\${id})]: \${nodes.length - keys.size} duplicated keys found\`));
|
||||
console.warn({ id, debugId: 6864, stack: instances });
|
||||
}
|
||||
}
|
||||
return children;
|
||||
};
|
||||
function onBeforeLeave(el) {
|
||||
el.style.top = \`\${el.offsetTop}px\`;
|
||||
el.style.left = \`\${el.offsetLeft}px\`;
|
||||
}
|
||||
function onLeaveCanceled(el) {
|
||||
el.style.top = "";
|
||||
el.style.left = "";
|
||||
}
|
||||
return () => h(
|
||||
defaultStore.state.animation ? TransitionGroup : "div",
|
||||
{
|
||||
class: {
|
||||
[$style["date-separated-list"]]: true,
|
||||
[$style["date-separated-list-nogap"]]: props.noGap,
|
||||
[$style["reversed"]]: props.reversed,
|
||||
[$style["direction-down"]]: props.direction === "down",
|
||||
[$style["direction-up"]]: props.direction === "up"
|
||||
},
|
||||
...defaultStore.state.animation ? {
|
||||
name: "list",
|
||||
tag: "div",
|
||||
onBeforeLeave,
|
||||
onLeaveCanceled
|
||||
} : {}
|
||||
},
|
||||
{ default: renderChildren }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const reversed = "xxiZh";
|
||||
const separator = "xxeDx";
|
||||
const date = "xxawD";
|
||||
const style0 = {
|
||||
"date-separated-list": "xfKPa",
|
||||
"date-separated-list-nogap": "xf9zr",
|
||||
"direction-up": "x7AeO",
|
||||
"direction-down": "xBIqc",
|
||||
reversed: reversed,
|
||||
separator: separator,
|
||||
date: date,
|
||||
"date-1": "xwtmh",
|
||||
"date-1-icon": "xsNPa",
|
||||
"date-2": "x1xvw",
|
||||
"date-2-icon": "x9ZiG"
|
||||
};
|
||||
|
||||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const MkDateSeparatedList = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
|
||||
|
||||
export { MkDateSeparatedList as M };
|
||||
`.slice(1), { ecmaVersion: 'latest', sourceType: 'module' });
|
||||
unwindCssModuleClassName(ast);
|
||||
expect(generate(ast)).toBe(`
|
||||
import {a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup} from './!~{002}~.js';
|
||||
import {d as defaultStore, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc} from './app-!~{001}~.js';
|
||||
function isDebuggerEnabled(id) {
|
||||
try {
|
||||
return localStorage.getItem(\`DEBUG_\${id}\`) !== null;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function stackTraceInstances() {
|
||||
let instance = getCurrentInstance();
|
||||
const stack = [];
|
||||
while (instance) {
|
||||
stack.push(instance);
|
||||
instance = instance.parent;
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
const _sfc_main = defineComponent({
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "down"
|
||||
},
|
||||
reversed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
noGap: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
ad: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, {slots, expose}) {
|
||||
const $style = useCssModule();
|
||||
function getDateText(time) {
|
||||
const date = new Date(time).getDate();
|
||||
const month = new Date(time).getMonth() + 1;
|
||||
return i18n.t("monthAndDay", {
|
||||
month: month.toString(),
|
||||
day: date.toString()
|
||||
});
|
||||
}
|
||||
if (props.items.length === 0) return;
|
||||
const renderChildrenImpl = () => props.items.map((item, i) => {
|
||||
if (!slots || !slots.default) return;
|
||||
const el = slots.default({
|
||||
item
|
||||
})[0];
|
||||
if (el.key == null && item.id) el.key = item.id;
|
||||
if (i !== props.items.length - 1 && new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate()) {
|
||||
const separator = h("div", {
|
||||
class: $style["separator"],
|
||||
key: item.id + ":separator"
|
||||
}, h("p", {
|
||||
class: $style["date"]
|
||||
}, [h("span", {
|
||||
class: $style["date-1"]
|
||||
}, [h("i", {
|
||||
class: \`ti ti-chevron-up \${$style["date-1-icon"]}\`
|
||||
}), getDateText(item.createdAt)]), h("span", {
|
||||
class: $style["date-2"]
|
||||
}, [getDateText(props.items[i + 1].createdAt), h("i", {
|
||||
class: \`ti ti-chevron-down \${$style["date-2-icon"]}\`
|
||||
})])]));
|
||||
return [el, separator];
|
||||
} else {
|
||||
if (props.ad && item._shouldInsertAd_) {
|
||||
return [h(MkAd, {
|
||||
key: item.id + ":ad",
|
||||
prefer: ["horizontal", "horizontal-big"]
|
||||
}), el];
|
||||
} else {
|
||||
return el;
|
||||
}
|
||||
}
|
||||
});
|
||||
const renderChildren = () => {
|
||||
const children = renderChildrenImpl();
|
||||
if (isDebuggerEnabled(6864)) {
|
||||
const nodes = children.flatMap(node => node ?? []);
|
||||
const keys = new Set(nodes.map(node => node.key));
|
||||
if (keys.size !== nodes.length) {
|
||||
const id = crypto.randomUUID();
|
||||
const instances = stackTraceInstances();
|
||||
toast(instances.reduce((a, c) => \`\${a} at \${c.type.name}\`, \`[DEBUG_6864 (\${id})]: \${nodes.length - keys.size} duplicated keys found\`));
|
||||
console.warn({
|
||||
id,
|
||||
debugId: 6864,
|
||||
stack: instances
|
||||
});
|
||||
}
|
||||
}
|
||||
return children;
|
||||
};
|
||||
function onBeforeLeave(el) {
|
||||
el.style.top = \`\${el.offsetTop}px\`;
|
||||
el.style.left = \`\${el.offsetLeft}px\`;
|
||||
}
|
||||
function onLeaveCanceled(el) {
|
||||
el.style.top = "";
|
||||
el.style.left = "";
|
||||
}
|
||||
return () => h(defaultStore.state.animation ? TransitionGroup : "div", {
|
||||
class: {
|
||||
[$style["date-separated-list"]]: true,
|
||||
[$style["date-separated-list-nogap"]]: props.noGap,
|
||||
[$style["reversed"]]: props.reversed,
|
||||
[$style["direction-down"]]: props.direction === "down",
|
||||
[$style["direction-up"]]: props.direction === "up"
|
||||
},
|
||||
...defaultStore.state.animation ? {
|
||||
name: "list",
|
||||
tag: "div",
|
||||
onBeforeLeave,
|
||||
onLeaveCanceled
|
||||
} : {}
|
||||
}, {
|
||||
default: renderChildren
|
||||
});
|
||||
}
|
||||
});
|
||||
const reversed = "xxiZh";
|
||||
const separator = "xxeDx";
|
||||
const date = "xxawD";
|
||||
const style0 = {
|
||||
"date-separated-list": "xfKPa",
|
||||
"date-separated-list-nogap": "xf9zr",
|
||||
"direction-up": "x7AeO",
|
||||
"direction-down": "xBIqc",
|
||||
reversed: reversed,
|
||||
separator: separator,
|
||||
date: date,
|
||||
"date-1": "xwtmh",
|
||||
"date-1-icon": "xsNPa",
|
||||
"date-2": "x1xvw",
|
||||
"date-2-icon": "x9ZiG"
|
||||
};
|
||||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const MkDateSeparatedList = _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
|
||||
export {MkDateSeparatedList as M};
|
||||
`.slice(1));
|
||||
});
|
|
@ -0,0 +1,275 @@
|
|||
import { generate } from 'astring';
|
||||
import * as estree from 'estree';
|
||||
import { walk } from '../node_modules/estree-walker/src/index.js';
|
||||
import type * as estreeWalker from 'estree-walker';
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
function isFalsyIdentifier(identifier: estree.Identifier): boolean {
|
||||
return identifier.name === 'undefined' || identifier.name === 'NaN';
|
||||
}
|
||||
|
||||
function normalizeClassWalker(tree: estree.Node): string | null {
|
||||
if (tree.type === 'Identifier') return isFalsyIdentifier(tree) ? '' : null;
|
||||
if (tree.type === 'Literal') return typeof tree.value === 'string' ? tree.value : '';
|
||||
if (tree.type === 'BinaryExpression') {
|
||||
if (tree.operator !== '+') return null;
|
||||
const left = normalizeClassWalker(tree.left);
|
||||
const right = normalizeClassWalker(tree.right);
|
||||
if (left === null || right === null) return null;
|
||||
return `${left}${right}`;
|
||||
}
|
||||
if (tree.type === 'TemplateLiteral') {
|
||||
if (tree.expressions.some((x) => x.type !== 'Literal' && (x.type !== 'Identifier' || !isFalsyIdentifier(x)))) return null;
|
||||
return tree.quasis.reduce((a, c, i) => {
|
||||
const v = i === tree.quasis.length - 1 ? '' : (tree.expressions[i] as Partial<estree.Literal>).value;
|
||||
return a + c.value.raw + (typeof v === 'string' ? v : '');
|
||||
}, '');
|
||||
}
|
||||
if (tree.type === 'ArrayExpression') {
|
||||
const values = tree.elements.map((treeNode) => {
|
||||
if (treeNode === null) return '';
|
||||
if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument);
|
||||
return normalizeClassWalker(treeNode);
|
||||
});
|
||||
if (values.some((x) => x === null)) return null;
|
||||
return values.join(' ');
|
||||
}
|
||||
if (tree.type === 'ObjectExpression') {
|
||||
const values = tree.properties.map((treeNode) => {
|
||||
if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument);
|
||||
let x = treeNode.value;
|
||||
let inveted = false;
|
||||
while (x.type === 'UnaryExpression' && x.operator === '!') {
|
||||
x = x.argument;
|
||||
inveted = !inveted;
|
||||
}
|
||||
if (x.type === 'Literal') {
|
||||
if (inveted === !x.value) {
|
||||
return treeNode.key.type === 'Identifier' ? treeNode.computed ? null : treeNode.key.name : treeNode.key.type === 'Literal' ? treeNode.key.value : '';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
if (x.type === 'Identifier') {
|
||||
if (inveted !== isFalsyIdentifier(x)) {
|
||||
return '';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (values.some((x) => x === null)) return null;
|
||||
return values.join(' ');
|
||||
}
|
||||
console.error(`Unexpected node type: ${tree.type}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeClass(tree: estree.Node): string | null {
|
||||
const walked = normalizeClassWalker(tree);
|
||||
return walked && walked.replace(/^\s+|\s+(?=\s)|\s+$/g, '');
|
||||
}
|
||||
|
||||
export function unwindCssModuleClassName(ast: estree.Node): void {
|
||||
(walk as typeof estreeWalker.walk)(ast, {
|
||||
enter(node, parent): void {
|
||||
if (parent?.type !== 'Program') return;
|
||||
if (node.type !== 'VariableDeclaration') return;
|
||||
if (node.declarations.length !== 1) return;
|
||||
if (node.declarations[0].id.type !== 'Identifier') return;
|
||||
const name = node.declarations[0].id.name;
|
||||
if (node.declarations[0].init?.type !== 'CallExpression') return;
|
||||
if (node.declarations[0].init.callee.type !== 'Identifier') return;
|
||||
if (node.declarations[0].init.callee.name !== '_export_sfc') return;
|
||||
if (node.declarations[0].init.arguments.length !== 2) return;
|
||||
if (node.declarations[0].init.arguments[0].type !== 'Identifier') return;
|
||||
const ident = node.declarations[0].init.arguments[0].name;
|
||||
if (!ident.startsWith('_sfc_main')) return;
|
||||
if (node.declarations[0].init.arguments[1].type !== 'ArrayExpression') return;
|
||||
if (node.declarations[0].init.arguments[1].elements.length === 0) return;
|
||||
const __cssModulesIndex = node.declarations[0].init.arguments[1].elements.findIndex((x) => {
|
||||
if (x?.type !== 'ArrayExpression') return false;
|
||||
if (x.elements.length !== 2) return false;
|
||||
if (x.elements[0]?.type !== 'Literal') return false;
|
||||
if (x.elements[0].value !== '__cssModules') return false;
|
||||
if (x.elements[1]?.type !== 'Identifier') return false;
|
||||
return true;
|
||||
});
|
||||
if (!~__cssModulesIndex) return;
|
||||
const cssModuleForestName = ((node.declarations[0].init.arguments[1].elements[__cssModulesIndex] as estree.ArrayExpression).elements[1] as estree.Identifier).name;
|
||||
const cssModuleForestNode = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
if (x.declarations.length !== 1) return false;
|
||||
if (x.declarations[0].id.type !== 'Identifier') return false;
|
||||
if (x.declarations[0].id.name !== cssModuleForestName) return false;
|
||||
if (x.declarations[0].init?.type !== 'ObjectExpression') return false;
|
||||
return true;
|
||||
}) as unknown as estree.VariableDeclaration;
|
||||
const moduleForest = new Map((cssModuleForestNode.declarations[0].init as estree.ObjectExpression).properties.flatMap((property) => {
|
||||
if (property.type !== 'Property') return [];
|
||||
if (property.key.type !== 'Literal') return [];
|
||||
if (property.value.type !== 'Identifier') return [];
|
||||
return [[property.key.value as string, property.value.name as string]];
|
||||
}));
|
||||
const sfcMain = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
if (x.declarations.length !== 1) return false;
|
||||
if (x.declarations[0].id.type !== 'Identifier') return false;
|
||||
if (x.declarations[0].id.name !== ident) return false;
|
||||
return true;
|
||||
}) as unknown as estree.VariableDeclaration;
|
||||
if (sfcMain.declarations[0].init?.type !== 'CallExpression') return;
|
||||
if (sfcMain.declarations[0].init.callee.type !== 'Identifier') return;
|
||||
if (sfcMain.declarations[0].init.callee.name !== 'defineComponent') return;
|
||||
if (sfcMain.declarations[0].init.arguments.length !== 1) return;
|
||||
if (sfcMain.declarations[0].init.arguments[0].type !== 'ObjectExpression') return;
|
||||
const setup = sfcMain.declarations[0].init.arguments[0].properties.find((x) => {
|
||||
if (x.type !== 'Property') return false;
|
||||
if (x.key.type !== 'Identifier') return false;
|
||||
if (x.key.name !== 'setup') return false;
|
||||
return true;
|
||||
}) as unknown as estree.Property;
|
||||
if (setup.value.type !== 'FunctionExpression') return;
|
||||
const render = setup.value.body.body.find((x) => {
|
||||
if (x.type !== 'ReturnStatement') return false;
|
||||
return true;
|
||||
}) as unknown as estree.ReturnStatement;
|
||||
if (render.argument?.type !== 'ArrowFunctionExpression') return;
|
||||
if (render.argument.params.length !== 2) return;
|
||||
const ctx = render.argument.params[0];
|
||||
if (ctx.type !== 'Identifier') return;
|
||||
if (ctx.name !== '_ctx') return;
|
||||
if (render.argument.body.type !== 'BlockStatement') return;
|
||||
for (const [key, value] of moduleForest) {
|
||||
const cssModuleTreeNode = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
if (x.declarations.length !== 1) return false;
|
||||
if (x.declarations[0].id.type !== 'Identifier') return false;
|
||||
if (x.declarations[0].id.name !== value) return false;
|
||||
return true;
|
||||
}) as unknown as estree.VariableDeclaration;
|
||||
if (cssModuleTreeNode.declarations[0].init?.type !== 'ObjectExpression') return;
|
||||
const moduleTree = new Map(cssModuleTreeNode.declarations[0].init.properties.flatMap((property) => {
|
||||
if (property.type !== 'Property') return [];
|
||||
const actualKey = property.key.type === 'Identifier' ? property.key.name : property.key.type === 'Literal' ? property.key.value : null;
|
||||
if (typeof actualKey !== 'string') return [];
|
||||
if (property.value.type === 'Literal') return [[actualKey, property.value.value as string]];
|
||||
if (property.value.type !== 'Identifier') return [];
|
||||
const labelledValue = property.value.name;
|
||||
const actualValue = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
if (x.declarations.length !== 1) return false;
|
||||
if (x.declarations[0].id.type !== 'Identifier') return false;
|
||||
if (x.declarations[0].id.name !== labelledValue) return false;
|
||||
return true;
|
||||
}) as unknown as estree.VariableDeclaration;
|
||||
if (actualValue.declarations[0].init?.type !== 'Literal') return [];
|
||||
return [[actualKey, actualValue.declarations[0].init.value as string]];
|
||||
}));
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'MemberExpression') return;
|
||||
if (childNode.object.type !== 'MemberExpression') return;
|
||||
if (childNode.object.object.type !== 'Identifier') return;
|
||||
if (childNode.object.object.name !== ctx.name) return;
|
||||
if (childNode.object.property.type !== 'Identifier') return;
|
||||
if (childNode.object.property.name !== key) return;
|
||||
if (childNode.property.type !== 'Identifier') return;
|
||||
const actualValue = moduleTree.get(childNode.property.name);
|
||||
if (actualValue === undefined) return;
|
||||
this.replace({
|
||||
type: 'Literal',
|
||||
value: actualValue,
|
||||
});
|
||||
},
|
||||
});
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'MemberExpression') return;
|
||||
if (childNode.object.type !== 'MemberExpression') return;
|
||||
if (childNode.object.object.type !== 'Identifier') return;
|
||||
if (childNode.object.object.name !== ctx.name) return;
|
||||
if (childNode.object.property.type !== 'Identifier') return;
|
||||
if (childNode.object.property.name !== key) return;
|
||||
if (childNode.property.type !== 'Identifier') return;
|
||||
console.error(`Undefined style detected: ${key}.${childNode.property.name} (in ${name})`);
|
||||
this.replace({
|
||||
type: 'Identifier',
|
||||
name: 'undefined',
|
||||
});
|
||||
},
|
||||
});
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'CallExpression') return;
|
||||
if (childNode.callee.type !== 'Identifier') return;
|
||||
if (childNode.callee.name !== 'normalizeClass') return;
|
||||
if (childNode.arguments.length !== 1) return;
|
||||
const normalized = normalizeClass(childNode.arguments[0]);
|
||||
if (normalized === null) return;
|
||||
this.replace({
|
||||
type: 'Literal',
|
||||
value: normalized,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
if (node.declarations[0].init.arguments[1].elements.length === 1) {
|
||||
this.replace({
|
||||
type: 'VariableDeclaration',
|
||||
declarations: [{
|
||||
type: 'VariableDeclarator',
|
||||
id: {
|
||||
type: 'Identifier',
|
||||
name: node.declarations[0].id.name,
|
||||
},
|
||||
init: {
|
||||
type: 'Identifier',
|
||||
name: ident,
|
||||
},
|
||||
}],
|
||||
kind: 'const',
|
||||
});
|
||||
} else {
|
||||
this.replace({
|
||||
type: 'VariableDeclaration',
|
||||
declarations: [{
|
||||
type: 'VariableDeclarator',
|
||||
id: {
|
||||
type: 'Identifier',
|
||||
name: node.declarations[0].id.name,
|
||||
},
|
||||
init: {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'Identifier',
|
||||
name: '_export_sfc',
|
||||
},
|
||||
arguments: [{
|
||||
type: 'Identifier',
|
||||
name: ident,
|
||||
}, {
|
||||
type: 'ArrayExpression',
|
||||
elements: node.declarations[0].init.arguments[1].elements.slice(0, __cssModulesIndex).concat(node.declarations[0].init.arguments[1].elements.slice(__cssModulesIndex + 1)),
|
||||
}],
|
||||
},
|
||||
}],
|
||||
kind: 'const',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function pluginUnwindCssModuleClassName(): Plugin {
|
||||
return {
|
||||
name: 'UnwindCssModuleClassName',
|
||||
renderChunk(code): { code: string } {
|
||||
const ast = this.parse(code) as unknown as estree.Node;
|
||||
unwindCssModuleClassName(ast);
|
||||
return { code: generate(ast) };
|
||||
},
|
||||
};
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vue-macros/reactivity-transform": "0.3.8",
|
||||
"@vue/compiler-sfc": "3.3.4",
|
||||
"astring": "1.8.5",
|
||||
"autosize": "6.0.1",
|
||||
"broadcast-channel": "4.20.2",
|
||||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"cropperjs": "2.0.0-beta.2",
|
||||
"date-fns": "2.30.0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
"eventemitter3": "5.0.1",
|
||||
"gsap": "3.11.5",
|
||||
"idb-keyval": "6.2.1",
|
||||
|
@ -116,7 +118,7 @@
|
|||
"@typescript-eslint/parser": "5.59.5",
|
||||
"@vitest/coverage-c8": "0.31.1",
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"astring": "1.8.5",
|
||||
"acorn": "^8.8.2",
|
||||
"chokidar-cli": "3.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.13.0",
|
||||
|
|
|
@ -3,7 +3,14 @@
|
|||
<div v-if="achievements" :class="$style.root">
|
||||
<div v-for="achievement in achievements" :key="achievement" :class="$style.achievement" class="_panel">
|
||||
<div :class="$style.icon">
|
||||
<div :class="[$style.iconFrame, $style['iconFrame_' + ACHIEVEMENT_BADGES[achievement.name].frame]]">
|
||||
<div
|
||||
:class="[$style.iconFrame, {
|
||||
[$style.iconFrame_bronze]: ACHIEVEMENT_BADGES[achievement.name].frame === 'bronze',
|
||||
[$style.iconFrame_silver]: ACHIEVEMENT_BADGES[achievement.name].frame === 'silver',
|
||||
[$style.iconFrame_gold]: ACHIEVEMENT_BADGES[achievement.name].frame === 'gold',
|
||||
[$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum',
|
||||
}]"
|
||||
>
|
||||
<div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
|
||||
<img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</li>
|
||||
<li tabindex="-1" :class="$style.item" @click="chooseUser()" @keydown="onKeydown">{{ i18n.ts.selectUser }}</li>
|
||||
</ol>
|
||||
<ol v-else-if="hashtags.length > 0" ref="suggests" :class="[$style.list, $style.hashtags]">
|
||||
<ol v-else-if="hashtags.length > 0" ref="suggests" :class="$style.list">
|
||||
<li v-for="hashtag in hashtags" tabindex="-1" :class="$style.item" @click="complete(type, hashtag)" @keydown="onKeydown">
|
||||
<span class="name">{{ hashtag }}</span>
|
||||
</li>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
@click="emit('click', $event)"
|
||||
@mousedown="onMousedown"
|
||||
>
|
||||
<div ref="ripples" :class="$style.ripples"></div>
|
||||
<div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div>
|
||||
<div :class="$style.content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
|||
:to="to"
|
||||
@mousedown="onMousedown"
|
||||
>
|
||||
<div ref="ripples" :class="$style.ripples"></div>
|
||||
<div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div>
|
||||
<div :class="$style.content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -26,9 +26,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, useCssModule } from 'vue';
|
||||
|
||||
const $style = useCssModule();
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
type?: 'button' | 'submit' | 'reset';
|
||||
|
@ -81,7 +79,7 @@ function onMousedown(evt: MouseEvent): void {
|
|||
const rect = target.getBoundingClientRect();
|
||||
|
||||
const ripple = document.createElement('div');
|
||||
ripple.classList.add($style.ripple);
|
||||
ripple.classList.add(ripples!.dataset.childrenClass!);
|
||||
ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
|
||||
ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div v-if="game.ready" :class="$style.game">
|
||||
<div :class="$style.cps" class="">{{ number(cps) }}cps</div>
|
||||
<div :class="$style.count" class=""><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div>
|
||||
<button v-click-anime class="_button" :class="$style.button" @click="onClick">
|
||||
<button v-click-anime class="_button" @click="onClick">
|
||||
<img src="/client-assets/cookie.png" :class="$style.img">
|
||||
</button>
|
||||
</div>
|
||||
|
@ -84,10 +84,6 @@ onUnmounted(() => {
|
|||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
||||
}
|
||||
|
||||
.img {
|
||||
max-width: 90px;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div ref="rootEl" class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.hideHeader]: !showHeader, [$style.scrollable]: scrollable, [$style.closed]: !showBody }]">
|
||||
<div ref="rootEl" class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.scrollable]: scrollable }]">
|
||||
<header v-if="showHeader" ref="headerEl" :class="$style.header">
|
||||
<div :class="$style.title">
|
||||
<span :class="$style.titleIcon"><slot name="icon"></slot></span>
|
||||
|
|
|
@ -36,7 +36,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
setup(props, { slots, expose }) {
|
||||
const $style = useCssModule();
|
||||
const $style = useCssModule(); // カスタムレンダラなので使っても大丈夫
|
||||
function getDateText(time: string) {
|
||||
const date = new Date(time).getDate();
|
||||
const month = new Date(time).getMonth() + 1;
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
<div v-if="icon" :class="$style.icon">
|
||||
<i :class="icon"></i>
|
||||
</div>
|
||||
<div v-else-if="!input && !select" :class="[$style.icon, $style['type_' + type]]">
|
||||
<div
|
||||
v-else-if="!input && !select"
|
||||
:class="[$style.icon, {
|
||||
[$style.type_success]: type === 'success',
|
||||
[$style.type_error]: type === 'error',
|
||||
[$style.type_warning]: type === 'warning',
|
||||
[$style.type_info]: type === 'info',
|
||||
}]"
|
||||
>
|
||||
<i v-if="type === 'success'" :class="$style.iconInner" class="ti ti-check"></i>
|
||||
<i v-else-if="type === 'error'" :class="$style.iconInner" class="ti ti-circle-x"></i>
|
||||
<i v-else-if="type === 'warning'" :class="$style.iconInner" class="ti ti-alert-triangle"></i>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div :class="[$style.header, { [$style.opened]: opened }]" class="_button" role="button" data-cy-folder-header @click="toggle">
|
||||
<div :class="$style.headerIcon"><slot name="icon"></slot></div>
|
||||
<div :class="$style.headerText">
|
||||
<div :class="$style.headerTextMain">
|
||||
<div>
|
||||
<MkCondensedLine :minScale="2 / 3"><slot name="label"></slot></MkCondensedLine>
|
||||
</div>
|
||||
<div :class="$style.headerTextSub">
|
||||
|
@ -185,10 +185,6 @@ onMounted(() => {
|
|||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.headerTextMain {
|
||||
|
||||
}
|
||||
|
||||
.headerTextSub {
|
||||
color: var(--fgTransparentWeak);
|
||||
font-size: .85em;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<TransitionGroup
|
||||
:duration="defaultStore.state.animation && props.transition?.duration || undefined"
|
||||
:enterActiveClass="defaultStore.state.animation && props.transition?.enterActiveClass || undefined"
|
||||
:leaveActiveClass="defaultStore.state.animation && (props.transition?.leaveActiveClass ?? $style['transition_leaveActive']) || undefined"
|
||||
:leaveActiveClass="defaultStore.state.animation && (props.transition?.leaveActiveClass ?? $style.transition_leaveActive) || undefined"
|
||||
:enterFromClass="defaultStore.state.animation && props.transition?.enterFromClass || undefined"
|
||||
:leaveToClass="defaultStore.state.animation && props.transition?.leaveToClass || undefined"
|
||||
:enterToClass="defaultStore.state.animation && props.transition?.enterToClass || undefined"
|
||||
|
@ -42,11 +42,10 @@ const workerPromise = new Promise<WorkerMultiDispatch | null>(resolve => {
|
|||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, shallowRef, useCssModule, watch } from 'vue';
|
||||
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { render } from 'buraha';
|
||||
import { defaultStore } from '@/store';
|
||||
const $style = useCssModule();
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
transition?: {
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
ref="gallery"
|
||||
:class="[
|
||||
$style.medias,
|
||||
count <= 4 ? $style['n' + count] : $style.nMany,
|
||||
$style[`n1${defaultStore.reactiveState.mediaListWithOneImageAppearance.value}`]
|
||||
count === 1 ? [$style.n1, {
|
||||
[$style.n116_9]: defaultStore.reactiveState.mediaListWithOneImageAppearance.value === '16_9',
|
||||
[$style.n11_1]: defaultStore.reactiveState.mediaListWithOneImageAppearance.value === '1_1',
|
||||
[$style.n12_3]: defaultStore.reactiveState.mediaListWithOneImageAppearance.value === '2_3',
|
||||
}] : count === 2 ? $style.n2 : count === 3 ? $style.n3 : count === 4 ? $style.n4 : $style.nMany,
|
||||
]"
|
||||
>
|
||||
<template v-for="media in mediaList.filter(media => previewable(media))">
|
||||
|
@ -20,7 +23,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, useCssModule, watch, shallowRef } from 'vue';
|
||||
import { onMounted, watch, shallowRef } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||
import PhotoSwipe from 'photoswipe';
|
||||
|
@ -37,8 +40,6 @@ const props = defineProps<{
|
|||
raw?: boolean;
|
||||
}>();
|
||||
|
||||
const $style = useCssModule();
|
||||
|
||||
const gallery = shallowRef<HTMLDivElement>();
|
||||
const pswpZIndex = os.claimZIndex('middle');
|
||||
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||
|
@ -96,7 +97,7 @@ onMounted(() => {
|
|||
return item;
|
||||
}),
|
||||
gallery: gallery.value,
|
||||
mainClass: $style.pswp,
|
||||
mainClass: 'pswp',
|
||||
children: '.image',
|
||||
thumbSelector: '.image',
|
||||
loop: false,
|
||||
|
@ -268,7 +269,7 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
|
|||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.pswp {
|
||||
:global(.pswp) {
|
||||
--pswp-root-z-index: var(--mk-pswp-root-z-index, 2000700) !important;
|
||||
--pswp-bg: var(--modalBg) !important;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }">
|
||||
<img :class="$style.icon" :src="`/avatar/@${username}@${host}`" alt="">
|
||||
<span>
|
||||
<span :class="$style.username">@{{ username }}</span>
|
||||
<span>@{{ username }}</span>
|
||||
<span v-if="(host != localHost) || defaultStore.state.showFullAcct" :class="$style.host">@{{ toUnicode(host) }}</span>
|
||||
</span>
|
||||
</MkA>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<span>{{ i18n.ts.none }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="childMenu" :class="$style.child">
|
||||
<div v-if="childMenu">
|
||||
<XChild ref="child" :items="childMenu" :targetElement="childTarget" :rootElement="itemsEl" showing @actioned="childActioned"/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,30 @@
|
|||
<template>
|
||||
<Transition
|
||||
:name="transitionName"
|
||||
:enterActiveClass="$style['transition_' + transitionName + '_enterActive']"
|
||||
:leaveActiveClass="$style['transition_' + transitionName + '_leaveActive']"
|
||||
:enterFromClass="$style['transition_' + transitionName + '_enterFrom']"
|
||||
:leaveToClass="$style['transition_' + transitionName + '_leaveTo']"
|
||||
:enterActiveClass="normalizeClass({
|
||||
[$style.transition_modalDrawer_enterActive]: transitionName === 'modal-drawer',
|
||||
[$style.transition_modalPopup_enterActive]: transitionName === 'modal-popup',
|
||||
[$style.transition_modal_enterActive]: transitionName === 'modal',
|
||||
[$style.transition_send_enterActive]: transitionName === 'send',
|
||||
})"
|
||||
:leaveActiveClass="normalizeClass({
|
||||
[$style.transition_modalDrawer_leaveActive]: transitionName === 'modal-drawer',
|
||||
[$style.transition_modalPopup_leaveActive]: transitionName === 'modal-popup',
|
||||
[$style.transition_modal_leaveActive]: transitionName === 'modal',
|
||||
[$style.transition_send_leaveActive]: transitionName === 'send',
|
||||
})"
|
||||
:enterFromClass="normalizeClass({
|
||||
[$style.transition_modalDrawer_enterFrom]: transitionName === 'modal-drawer',
|
||||
[$style.transition_modalPopup_enterFrom]: transitionName === 'modal-popup',
|
||||
[$style.transition_modal_enterFrom]: transitionName === 'modal',
|
||||
[$style.transition_send_enterFrom]: transitionName === 'send',
|
||||
})"
|
||||
:leaveToClass="normalizeClass({
|
||||
[$style.transition_modalDrawer_leaveTo]: transitionName === 'modal-drawer',
|
||||
[$style.transition_modalPopup_leaveTo]: transitionName === 'modal-popup',
|
||||
[$style.transition_modal_leaveTo]: transitionName === 'modal',
|
||||
[$style.transition_send_leaveTo]: transitionName === 'send',
|
||||
})"
|
||||
:duration="transitionDuration" appear @afterLeave="emit('closed')" @enter="emit('opening')" @afterEnter="onOpened"
|
||||
>
|
||||
<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" :class="[$style.root, { [$style.drawer]: type === 'drawer', [$style.dialog]: type === 'dialog', [$style.popup]: type === 'popup' }]" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
||||
|
@ -17,7 +37,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, watch, provide, onUnmounted } from 'vue';
|
||||
import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import { isTouchUsing } from '@/scripts/touch';
|
||||
import { defaultStore } from '@/store';
|
||||
|
@ -345,8 +365,8 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
|
||||
.transition_modal-popup_enterActive,
|
||||
.transition_modal-popup_leaveActive {
|
||||
.transition_modalPopup_enterActive,
|
||||
.transition_modalPopup_leaveActive {
|
||||
> .bg {
|
||||
transition: opacity 0.1s !important;
|
||||
}
|
||||
|
@ -356,8 +376,8 @@ defineExpose({
|
|||
transition: opacity 0.1s cubic-bezier(0, 0, 0.2, 1), transform 0.1s cubic-bezier(0, 0, 0.2, 1) !important;
|
||||
}
|
||||
}
|
||||
.transition_modal-popup_enterFrom,
|
||||
.transition_modal-popup_leaveTo {
|
||||
.transition_modalPopup_enterFrom,
|
||||
.transition_modalPopup_leaveTo {
|
||||
> .bg {
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -370,7 +390,7 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
|
||||
.transition_modal-drawer_enterActive {
|
||||
.transition_modalDrawer_enterActive {
|
||||
> .bg {
|
||||
transition: opacity 0.2s !important;
|
||||
}
|
||||
|
@ -379,7 +399,7 @@ defineExpose({
|
|||
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
|
||||
}
|
||||
}
|
||||
.transition_modal-drawer_leaveActive {
|
||||
.transition_modalDrawer_leaveActive {
|
||||
> .bg {
|
||||
transition: opacity 0.2s !important;
|
||||
}
|
||||
|
@ -388,8 +408,8 @@ defineExpose({
|
|||
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
|
||||
}
|
||||
}
|
||||
.transition_modal-drawer_enterFrom,
|
||||
.transition_modal-drawer_leaveTo {
|
||||
.transition_modalDrawer_enterFrom,
|
||||
.transition_modalDrawer_leaveTo {
|
||||
> .bg {
|
||||
opacity: 0;
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@
|
|||
<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
|
||||
<MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/>
|
||||
<div :class="$style.main">
|
||||
<MkNoteHeader :class="$style.header" :note="appearNote" :mini="true"/>
|
||||
<MkInstanceTicker v-if="showTicker" :class="$style.ticker" :instance="appearNote.user.instance"/>
|
||||
<MkNoteHeader :note="appearNote" :mini="true"/>
|
||||
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
||||
<div style |