1
This commit is contained in:
@ -19,6 +19,7 @@ interface Icon {
|
||||
text?: string;
|
||||
bgColor?: string;
|
||||
size?: GridItemSize;
|
||||
groupId?: string;
|
||||
}
|
||||
|
||||
type WidgetSize = GridItemSize;
|
||||
@ -27,6 +28,7 @@ interface Widget {
|
||||
component: string;
|
||||
size: WidgetSize;
|
||||
data?: any;
|
||||
groupId?: string;
|
||||
}
|
||||
|
||||
interface GridOrderEntry {
|
||||
@ -39,6 +41,11 @@ type OrderedGridItem =
|
||||
| { type: 'widget'; id: string; widget: Widget };
|
||||
|
||||
const GRID_ORDER_STORAGE_KEY = 'itab_grid_order';
|
||||
const DEFAULT_GROUP_ID = 'home';
|
||||
|
||||
const props = defineProps<{
|
||||
activeGroupId?: string | null;
|
||||
}>();
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
const widgetsStore = useWidgetsStore();
|
||||
@ -59,6 +66,22 @@ const previousIconIds = ref(new Set<string>());
|
||||
const previousWidgetIds = ref(new Set<string>());
|
||||
const layoutAnimationMs = 240;
|
||||
const layoutEasing = 'cubic-bezier(0.4, 0, 0.2, 1)';
|
||||
const normalizeGroupId = (groupId?: string | null) => groupId ?? DEFAULT_GROUP_ID;
|
||||
const activeGroupId = computed(() => normalizeGroupId(props.activeGroupId));
|
||||
|
||||
const normalizeGridOrder = (order: GridOrderEntry[]) => {
|
||||
const seen = new Set<string>();
|
||||
const normalized: GridOrderEntry[] = [];
|
||||
for (const entry of order) {
|
||||
if (!entry || typeof entry.id !== 'string') continue;
|
||||
if (entry.type !== 'icon' && entry.type !== 'widget') continue;
|
||||
const key = `${entry.type}:${entry.id}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
normalized.push(entry);
|
||||
}
|
||||
return normalized;
|
||||
};
|
||||
|
||||
const buildDefaultOrder = (): GridOrderEntry[] => [
|
||||
...widgetsStore.widgets.map(widget => ({ id: widget.id, type: 'widget' as const })),
|
||||
@ -85,7 +108,7 @@ const loadGridOrder = () => {
|
||||
try {
|
||||
const parsed = JSON.parse(saved) as GridOrderEntry[];
|
||||
if (Array.isArray(parsed)) {
|
||||
gridOrder.value = parsed.filter(item => item?.id && (item.type === 'icon' || item.type === 'widget'));
|
||||
gridOrder.value = normalizeGridOrder(parsed);
|
||||
}
|
||||
} catch {
|
||||
gridOrder.value = [];
|
||||
@ -100,26 +123,36 @@ const loadGridOrder = () => {
|
||||
const ensureOrderConsistency = () => {
|
||||
const iconIds = new Set(layoutStore.icons.map(icon => icon.id));
|
||||
const widgetIds = new Set(widgetsStore.widgets.map(widget => widget.id));
|
||||
const seen = new Set<string>();
|
||||
const nextOrder: GridOrderEntry[] = [];
|
||||
|
||||
const pushEntry = (entry: GridOrderEntry) => {
|
||||
const key = `${entry.type}:${entry.id}`;
|
||||
if (seen.has(key)) return;
|
||||
seen.add(key);
|
||||
nextOrder.push(entry);
|
||||
};
|
||||
|
||||
for (const entry of gridOrder.value) {
|
||||
if (entry.type === 'icon' && iconIds.has(entry.id)) {
|
||||
nextOrder.push(entry);
|
||||
pushEntry(entry);
|
||||
}
|
||||
if (entry.type === 'widget' && widgetIds.has(entry.id)) {
|
||||
nextOrder.push(entry);
|
||||
pushEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
for (const widget of widgetsStore.widgets) {
|
||||
if (!nextOrder.some(item => item.type === 'widget' && item.id === widget.id)) {
|
||||
nextOrder.push({ id: widget.id, type: 'widget' });
|
||||
const key = `widget:${widget.id}`;
|
||||
if (!seen.has(key)) {
|
||||
pushEntry({ id: widget.id, type: 'widget' });
|
||||
}
|
||||
}
|
||||
|
||||
for (const icon of layoutStore.icons) {
|
||||
if (!nextOrder.some(item => item.type === 'icon' && item.id === icon.id)) {
|
||||
nextOrder.push({ id: icon.id, type: 'icon' });
|
||||
const key = `icon:${icon.id}`;
|
||||
if (!seen.has(key)) {
|
||||
pushEntry({ id: icon.id, type: 'icon' });
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,6 +170,9 @@ const ensureOrderConsistency = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const isInActiveGroup = (groupId?: string | null) =>
|
||||
normalizeGroupId(groupId) === activeGroupId.value;
|
||||
|
||||
const orderedItems = computed<OrderedGridItem[]>(() => {
|
||||
const iconsById = new Map(layoutStore.icons.map(icon => [icon.id, icon]));
|
||||
const widgetsById = new Map(widgetsStore.widgets.map(widget => [widget.id, widget]));
|
||||
@ -146,13 +182,13 @@ const orderedItems = computed<OrderedGridItem[]>(() => {
|
||||
for (const entry of gridOrder.value) {
|
||||
if (entry.type === 'icon') {
|
||||
const icon = iconsById.get(entry.id);
|
||||
if (icon) {
|
||||
if (icon && isInActiveGroup(icon.groupId)) {
|
||||
items.push({ type: 'icon', id: icon.id, icon });
|
||||
used.add(`icon:${icon.id}`);
|
||||
}
|
||||
} else {
|
||||
const widget = widgetsById.get(entry.id);
|
||||
if (widget) {
|
||||
if (widget && isInActiveGroup(widget.groupId)) {
|
||||
items.push({ type: 'widget', id: widget.id, widget });
|
||||
used.add(`widget:${widget.id}`);
|
||||
}
|
||||
@ -161,14 +197,14 @@ const orderedItems = computed<OrderedGridItem[]>(() => {
|
||||
|
||||
for (const widget of widgetsStore.widgets) {
|
||||
const key = `widget:${widget.id}`;
|
||||
if (!used.has(key)) {
|
||||
if (!used.has(key) && isInActiveGroup(widget.groupId)) {
|
||||
items.push({ type: 'widget', id: widget.id, widget });
|
||||
}
|
||||
}
|
||||
|
||||
for (const icon of layoutStore.icons) {
|
||||
const key = `icon:${icon.id}`;
|
||||
if (!used.has(key)) {
|
||||
if (!used.has(key) && isInActiveGroup(icon.groupId)) {
|
||||
items.push({ type: 'icon', id: icon.id, icon });
|
||||
}
|
||||
}
|
||||
@ -222,7 +258,36 @@ const persistOrderFromGrid = async () => {
|
||||
}
|
||||
}
|
||||
if (nextOrder.length) {
|
||||
applyGridOrder(nextOrder, true);
|
||||
if (!gridOrder.value.length) {
|
||||
applyGridOrder(nextOrder, true);
|
||||
} else {
|
||||
const entryKey = (entry: GridOrderEntry) => `${entry.type}:${entry.id}`;
|
||||
const visibleKeys = new Set(nextOrder.map(entryKey));
|
||||
const visibleQueue = [...nextOrder];
|
||||
const merged: GridOrderEntry[] = [];
|
||||
|
||||
for (const entry of gridOrder.value) {
|
||||
if (visibleKeys.has(entryKey(entry))) {
|
||||
const nextEntry = visibleQueue.shift();
|
||||
if (nextEntry) {
|
||||
merged.push(nextEntry);
|
||||
}
|
||||
} else {
|
||||
merged.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
const mergedKeys = new Set(merged.map(entryKey));
|
||||
for (const entry of visibleQueue) {
|
||||
const key = entryKey(entry);
|
||||
if (!mergedKeys.has(key)) {
|
||||
merged.push(entry);
|
||||
mergedKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
applyGridOrder(merged, true);
|
||||
}
|
||||
}
|
||||
await nextTick();
|
||||
grid.value?.synchronize();
|
||||
@ -247,7 +312,11 @@ const syncGridItems = () => {
|
||||
const refreshLayout = async (instant = false) => {
|
||||
await nextTick();
|
||||
syncGridItems();
|
||||
grid.value?.synchronize();
|
||||
grid.value?.refreshItems();
|
||||
if (grid.value?._settings?.layout) {
|
||||
grid.value._settings.layout.fillGaps = true;
|
||||
}
|
||||
if (instant) {
|
||||
grid.value?.layout(true);
|
||||
} else {
|
||||
@ -283,6 +352,7 @@ const syncSizeMap = (
|
||||
|
||||
onMounted(async () => {
|
||||
loadGridOrder();
|
||||
ensureOrderConsistency();
|
||||
previousIconIds.value = new Set(layoutStore.icons.map(icon => icon.id));
|
||||
previousWidgetIds.value = new Set(widgetsStore.widgets.map(widget => widget.id));
|
||||
await nextTick();
|
||||
@ -296,7 +366,7 @@ onMounted(async () => {
|
||||
},
|
||||
dragSort: true,
|
||||
layout: {
|
||||
fillGaps: settingsStore.autoAlign,
|
||||
fillGaps: true,
|
||||
rounding: true,
|
||||
},
|
||||
layoutDuration: layoutAnimationMs,
|
||||
@ -388,12 +458,10 @@ watch(
|
||||
);
|
||||
|
||||
watch(
|
||||
() => settingsStore.autoAlign,
|
||||
value => {
|
||||
() => props.activeGroupId,
|
||||
() => {
|
||||
if (!grid.value) return;
|
||||
// Muuri has no public setter for fillGaps; update internal setting and relayout.
|
||||
grid.value._settings.layout.fillGaps = value;
|
||||
grid.value.layout(true);
|
||||
refreshLayout(true);
|
||||
}
|
||||
);
|
||||
|
||||
@ -475,8 +543,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.grid-item.is-resized {
|
||||
overflow: hidden;
|
||||
border-radius: $border-radius-small;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.grid-item.is-entering {
|
||||
|
||||
Reference in New Issue
Block a user