All files / src/packlets/editor-core/slices level-slice.ts

88.88% Statements 32/36
37.5% Branches 6/16
84.61% Functions 11/13
88.57% Lines 31/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                    1x   51x 51x     111x 111x     22x 22x                   22x       109x 22x 22x                     109x       2x 2x   2x     2x       2x 2x 2x       1x 1x 1x 1x 1x       2x 2x 1x   1x   2x 2x       51x      
import { atom } from "nanostores";
import { createNanoEvents } from "nanoevents";
import { Slice } from "../slice";
import { ProjectSlice } from "./project-slice";
import { CHART_REF, LEVEL } from "../components";
import { EntityBuilder } from "../../entity-manager";
import type { Entity } from "../../entity-manager";
import type { LevelInfo } from "../types";
 
export class LevelSlice extends Slice {
  static readonly sliceKey = "level";
 
  $hiddenLevelIds = atom<Set<string>>(new Set());
  private events = createNanoEvents<{ levelsChanged: () => void }>();
 
  getLevelEntitiesForChart(chartId: string): Entity[] {
    const em = this.ctx.get(ProjectSlice).entityManager;
    return em
      .entitiesWithComponent(LEVEL)
      .filter((entity) => {
        const ref = em.getComponent(entity, CHART_REF);
        return ref?.chartId === chartId;
      })
      .sort((a, b) => {
        const levelA = em.getComponent(a, LEVEL);
        const levelB = em.getComponent(b, LEVEL);
        return (levelA?.sortOrder ?? 0) - (levelB?.sortOrder ?? 0);
      });
  }
 
  isLevelHidden(levelId: string): boolean {
    return this.$hiddenLevelIds.get().has(levelId);
  }
 
  getLevelsForChart(chartId: string): LevelInfo[] {
    return this.getLevelEntitiesForChart(chartId).map((entity) => {
      const level = this.ctx.get(ProjectSlice).entityManager.getComponent(entity, LEVEL);
      return {
        id: entity.id,
        name: level?.name ?? "Untitled",
        mode: level?.mode ?? "beat-7k",
        sortOrder: level?.sortOrder ?? 0,
        visible: !this.isLevelHidden(entity.id),
      };
    });
  }
 
  getVisibleLevels(chartId: string): LevelInfo[] {
    return this.getLevelsForChart(chartId).filter((l) => l.visible);
  }
 
  addLevel(chartId: string, name: string, mode: string): string {
    const em = this.ctx.get(ProjectSlice).entityManager;
    const existing = this.getLevelEntitiesForChart(chartId);
    const maxOrder =
      existing.length > 0
        ? Math.max(...existing.map((e) => em.getComponent(e, LEVEL)?.sortOrder ?? 0))
        : -1;
    const level = new EntityBuilder()
      .with(LEVEL, { name, mode, sortOrder: maxOrder + 1 })
      .with(CHART_REF, { chartId })
      .build();
    em.insert(level);
    this.events.emit("levelsChanged");
    return level.id;
  }
 
  removeLevel(levelId: string): void {
    this.ctx.get(ProjectSlice).entityManager.remove(levelId);
    const hidden = new Set(this.$hiddenLevelIds.get());
    hidden.delete(levelId);
    this.$hiddenLevelIds.set(hidden);
    this.events.emit("levelsChanged");
  }
 
  toggleLevelVisibility(levelId: string): void {
    const hidden = new Set(this.$hiddenLevelIds.get());
    if (hidden.has(levelId)) {
      hidden.delete(levelId);
    } else {
      hidden.add(levelId);
    }
    this.$hiddenLevelIds.set(hidden);
    this.events.emit("levelsChanged");
  }
 
  onLevelsChanged(cb: () => void): () => void {
    return this.events.on("levelsChanged", cb);
  }
}