Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
228a2603e8 | ||
|
|
27ff8ba1ad |
@@ -112,7 +112,7 @@ function EditableName({
|
||||
onClick={startEditing}
|
||||
title="Rename"
|
||||
aria-label="Rename"
|
||||
className="inline-flex shrink-0 items-center rounded p-0.5 text-muted-foreground opacity-0 pointer-coarse:opacity-100 transition-colors transition-opacity hover:bg-hover-neutral-bg hover:text-hover-neutral focus:opacity-100 group-hover:opacity-100"
|
||||
className="inline-flex pointer-coarse:w-auto w-0 shrink-0 items-center overflow-hidden pointer-coarse:overflow-visible rounded p-0.5 text-muted-foreground opacity-0 pointer-coarse:opacity-100 transition-all duration-150 hover:bg-hover-neutral-bg hover:text-hover-neutral focus:w-auto focus:overflow-visible focus:opacity-100 group-hover:w-auto group-hover:overflow-visible group-hover:opacity-100"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
CONDITION_DEFINITIONS,
|
||||
type ConditionId,
|
||||
getConditionDescription,
|
||||
getConditionsForEdition,
|
||||
} from "@initiative/domain";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import {
|
||||
@@ -17,7 +17,9 @@ import {
|
||||
Heart,
|
||||
Link,
|
||||
Moon,
|
||||
ShieldMinus,
|
||||
Siren,
|
||||
Snail,
|
||||
Sparkles,
|
||||
ZapOff,
|
||||
} from "lucide-react";
|
||||
@@ -41,6 +43,8 @@ const ICON_MAP: Record<string, LucideIcon> = {
|
||||
Droplet,
|
||||
ArrowDown,
|
||||
Link,
|
||||
ShieldMinus,
|
||||
Snail,
|
||||
Sparkles,
|
||||
Moon,
|
||||
};
|
||||
@@ -56,6 +60,7 @@ const COLOR_CLASSES: Record<string, string> = {
|
||||
slate: "text-slate-400",
|
||||
green: "text-green-400",
|
||||
indigo: "text-indigo-400",
|
||||
sky: "text-sky-400",
|
||||
};
|
||||
|
||||
interface ConditionPickerProps {
|
||||
@@ -110,6 +115,7 @@ export function ConditionPicker({
|
||||
}, [onClose]);
|
||||
|
||||
const { edition } = useRulesEditionContext();
|
||||
const conditions = getConditionsForEdition(edition);
|
||||
const active = new Set(activeConditions ?? []);
|
||||
|
||||
return createPortal(
|
||||
@@ -122,7 +128,7 @@ export function ConditionPicker({
|
||||
: { visibility: "hidden" as const }
|
||||
}
|
||||
>
|
||||
{CONDITION_DEFINITIONS.map((def) => {
|
||||
{conditions.map((def) => {
|
||||
const Icon = ICON_MAP[def.iconName];
|
||||
if (!Icon) return null;
|
||||
const isActive = active.has(def.id);
|
||||
|
||||
@@ -18,7 +18,9 @@ import {
|
||||
Link,
|
||||
Moon,
|
||||
Plus,
|
||||
ShieldMinus,
|
||||
Siren,
|
||||
Snail,
|
||||
Sparkles,
|
||||
ZapOff,
|
||||
} from "lucide-react";
|
||||
@@ -40,6 +42,8 @@ const ICON_MAP: Record<string, LucideIcon> = {
|
||||
Droplet,
|
||||
ArrowDown,
|
||||
Link,
|
||||
ShieldMinus,
|
||||
Snail,
|
||||
Sparkles,
|
||||
Moon,
|
||||
};
|
||||
@@ -55,6 +59,7 @@ const COLOR_CLASSES: Record<string, string> = {
|
||||
slate: "text-slate-400",
|
||||
green: "text-green-400",
|
||||
indigo: "text-indigo-400",
|
||||
sky: "text-sky-400",
|
||||
};
|
||||
|
||||
interface ConditionTagsProps {
|
||||
@@ -103,7 +108,7 @@ export function ConditionTags({
|
||||
type="button"
|
||||
title="Add condition"
|
||||
aria-label="Add condition"
|
||||
className="inline-flex items-center rounded p-0.5 text-muted-foreground opacity-0 pointer-coarse:opacity-100 transition-colors transition-opacity hover:bg-hover-neutral-bg hover:text-hover-neutral focus:opacity-100 group-hover:opacity-100"
|
||||
className="inline-flex pointer-coarse:w-auto w-0 items-center overflow-hidden pointer-coarse:overflow-visible rounded p-0.5 text-muted-foreground opacity-0 pointer-coarse:opacity-100 transition-all duration-150 hover:bg-hover-neutral-bg hover:text-hover-neutral focus:w-auto focus:overflow-visible focus:opacity-100 group-hover:w-auto group-hover:overflow-visible group-hover:opacity-100"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onOpenPicker();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
CONDITION_DEFINITIONS,
|
||||
getConditionDescription,
|
||||
getConditionsForEdition,
|
||||
} from "../conditions.js";
|
||||
|
||||
function findCondition(id: string) {
|
||||
@@ -25,13 +26,27 @@ describe("getConditionDescription", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("every condition has both descriptions", () => {
|
||||
for (const def of CONDITION_DEFINITIONS) {
|
||||
it("universal conditions have both descriptions", () => {
|
||||
const universal = CONDITION_DEFINITIONS.filter(
|
||||
(d) => d.edition === undefined,
|
||||
);
|
||||
expect(universal.length).toBeGreaterThan(0);
|
||||
for (const def of universal) {
|
||||
expect(def.description).toBeTruthy();
|
||||
expect(def.description5e).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("edition-specific conditions have their edition description", () => {
|
||||
const sapped = findCondition("sapped");
|
||||
expect(sapped.description).toBeTruthy();
|
||||
expect(sapped.edition).toBe("5.5e");
|
||||
|
||||
const slowed = findCondition("slowed");
|
||||
expect(slowed.description).toBeTruthy();
|
||||
expect(slowed.edition).toBe("5.5e");
|
||||
});
|
||||
|
||||
it("conditions with identical rules share the same text", () => {
|
||||
const blinded = findCondition("blinded");
|
||||
expect(blinded.description).toBe(blinded.description5e);
|
||||
@@ -42,3 +57,26 @@ describe("getConditionDescription", () => {
|
||||
expect(exhaustion.description).not.toBe(exhaustion.description5e);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getConditionsForEdition", () => {
|
||||
it("includes sapped and slowed for 5.5e", () => {
|
||||
const conditions = getConditionsForEdition("5.5e");
|
||||
const ids = conditions.map((d) => d.id);
|
||||
expect(ids).toContain("sapped");
|
||||
expect(ids).toContain("slowed");
|
||||
});
|
||||
|
||||
it("excludes sapped and slowed for 5e", () => {
|
||||
const conditions = getConditionsForEdition("5e");
|
||||
const ids = conditions.map((d) => d.id);
|
||||
expect(ids).not.toContain("sapped");
|
||||
expect(ids).not.toContain("slowed");
|
||||
});
|
||||
|
||||
it("includes universal conditions for both editions", () => {
|
||||
const ids5e = getConditionsForEdition("5e").map((d) => d.id);
|
||||
const ids55e = getConditionsForEdition("5.5e").map((d) => d.id);
|
||||
expect(ids5e).toContain("blinded");
|
||||
expect(ids55e).toContain("blinded");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,8 @@ export type ConditionId =
|
||||
| "poisoned"
|
||||
| "prone"
|
||||
| "restrained"
|
||||
| "sapped"
|
||||
| "slowed"
|
||||
| "stunned"
|
||||
| "unconscious";
|
||||
|
||||
@@ -24,6 +26,8 @@ export interface ConditionDefinition {
|
||||
readonly description5e: string;
|
||||
readonly iconName: string;
|
||||
readonly color: string;
|
||||
/** When set, the condition only appears in this edition's picker. */
|
||||
readonly edition?: RulesEdition;
|
||||
}
|
||||
|
||||
export function getConditionDescription(
|
||||
@@ -159,6 +163,26 @@ export const CONDITION_DEFINITIONS: readonly ConditionDefinition[] = [
|
||||
iconName: "Link",
|
||||
color: "neutral",
|
||||
},
|
||||
{
|
||||
id: "sapped",
|
||||
label: "Sapped",
|
||||
description:
|
||||
"Disadvantage on next attack roll before the start of your next turn. (Weapon Mastery: Sap)",
|
||||
description5e: "",
|
||||
iconName: "ShieldMinus",
|
||||
color: "amber",
|
||||
edition: "5.5e",
|
||||
},
|
||||
{
|
||||
id: "slowed",
|
||||
label: "Slowed",
|
||||
description:
|
||||
"Speed reduced by 10 ft. until the start of your next turn. (Weapon Mastery: Slow)",
|
||||
description5e: "",
|
||||
iconName: "Snail",
|
||||
color: "sky",
|
||||
edition: "5.5e",
|
||||
},
|
||||
{
|
||||
id: "stunned",
|
||||
label: "Stunned",
|
||||
@@ -184,3 +208,11 @@ export const CONDITION_DEFINITIONS: readonly ConditionDefinition[] = [
|
||||
export const VALID_CONDITION_IDS: ReadonlySet<string> = new Set(
|
||||
CONDITION_DEFINITIONS.map((d) => d.id),
|
||||
);
|
||||
|
||||
export function getConditionsForEdition(
|
||||
edition: RulesEdition,
|
||||
): readonly ConditionDefinition[] {
|
||||
return CONDITION_DEFINITIONS.filter(
|
||||
(d) => d.edition === undefined || d.edition === edition,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export {
|
||||
type ConditionDefinition,
|
||||
type ConditionId,
|
||||
getConditionDescription,
|
||||
getConditionsForEdition,
|
||||
type RulesEdition,
|
||||
VALID_CONDITION_IDS,
|
||||
} from "./conditions.js";
|
||||
|
||||
Reference in New Issue
Block a user