mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 21:28:59 +00:00
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+92
-42
@@ -10,14 +10,16 @@
|
||||
* to SCALE: a sofa is a 2.2 x 0.9 m rectangle with a back along one long
|
||||
* side. Putting a 24x24 pictogram into that rectangle stretches a drawing
|
||||
* that was never meant to be stretched, and the result reads as an icon lying
|
||||
* on the floor, not as a piece of furniture. So the symbols below are drawn
|
||||
* here, in a UNIT BOX, and generated at the shape's real size — which also
|
||||
* means the stroke is a plain `stroke-width` in render units, exactly like
|
||||
* every other decor shape, instead of a `vector-effect` hack fighting a
|
||||
* non-uniform scale.
|
||||
* on the floor, not as a piece of furniture. The primary library is therefore
|
||||
* generated from House Plan's own top-view SVG pack. A small compatibility
|
||||
* set that is absent from the pack stays expressed as unit-box primitives.
|
||||
* Both forms resolve to one path plus its native coordinate box; the renderer
|
||||
* scales that box to the stored real-world size and keeps the configured decor
|
||||
* stroke stable with `vector-effect="non-scaling-stroke"`.
|
||||
*
|
||||
* THE CONVENTION EVERY SYMBOL OBEYS:
|
||||
* - the box is `0..1 x 0..1`, x to the right, y DOWN (SVG);
|
||||
* - x points right and y points DOWN (SVG); generated art keeps its native
|
||||
* viewBox while legacy primitives use `0..1 x 0..1`;
|
||||
* - `y = 0` is the BACK of the object — the side that goes against a wall.
|
||||
* That is what makes the wall magnet meaningful: a sofa's back, a bed's
|
||||
* headboard, a wardrobe's rear panel and a worktop's edge all mean the
|
||||
@@ -27,6 +29,7 @@
|
||||
*/
|
||||
|
||||
import { NORM_W, GRID_PITCH, CANVAS_LIMIT } from './space-geometry';
|
||||
import { GENERATED_FURNITURE_PLAN } from './furniture-plan-art.generated';
|
||||
|
||||
/** Groups the palette shows, in the order it shows them. */
|
||||
export const FURNITURE_GROUPS = ['furniture', 'appliance', 'sanitary', 'other'] as const;
|
||||
@@ -53,11 +56,21 @@ export interface FurnitureSymbol {
|
||||
/** Stable id — it is what the config stores and what `data-symbol` carries. */
|
||||
id: string;
|
||||
group: FurnitureGroup;
|
||||
/** Category tile in the two-level editor palette. */
|
||||
category: string;
|
||||
/** Default REAL size in centimetres: width ALONG the back edge x depth. */
|
||||
w: number;
|
||||
h: number;
|
||||
/** The drawing, in the unit box described above. */
|
||||
g: Prim[];
|
||||
/** Legacy drawing retained for symbols absent from the designer pack. */
|
||||
g?: Prim[];
|
||||
/** Designer drawing with its native SVG viewBox. */
|
||||
art?: FurnitureGraphic;
|
||||
}
|
||||
|
||||
export interface FurnitureGraphic {
|
||||
d: string;
|
||||
viewW: number;
|
||||
viewH: number;
|
||||
}
|
||||
|
||||
// The outline every boxy symbol starts from, named once so the table reads.
|
||||
@@ -68,136 +81,136 @@ const box = (): Prim => ['r', 0, 0, 1, 1];
|
||||
* DEFAULTS, not limits — the palette lets the user type over both numbers
|
||||
* before placing, and the corner handles change them afterwards.
|
||||
*/
|
||||
export const FURNITURE: FurnitureSymbol[] = [
|
||||
const LEGACY_FURNITURE: FurnitureSymbol[] = [
|
||||
// ------------------------------- мебель --------------------------------
|
||||
{ id: 'sofa', group: 'furniture', w: 220, h: 90, g: [
|
||||
{ id: 'sofa', group: 'furniture', category: 'sofa', w: 220, h: 90, g: [
|
||||
box(),
|
||||
['l', 0.09, 0.26, 0.91, 0.26], // the back cushion
|
||||
['l', 0.09, 0.26, 0.09, 1], // armrests
|
||||
['l', 0.91, 0.26, 0.91, 1],
|
||||
['l', 0.5, 0.26, 0.5, 1], // two seats
|
||||
] },
|
||||
{ id: 'armchair', group: 'furniture', w: 90, h: 85, g: [
|
||||
{ id: 'armchair', group: 'furniture', category: 'armchair', w: 90, h: 85, g: [
|
||||
box(),
|
||||
['l', 0.14, 0.28, 0.86, 0.28],
|
||||
['l', 0.14, 0.28, 0.14, 1],
|
||||
['l', 0.86, 0.28, 0.86, 1],
|
||||
] },
|
||||
{ id: 'coffee_table', group: 'furniture', w: 110, h: 60, g: [
|
||||
{ id: 'coffee_table', group: 'furniture', category: 'coffee_table', w: 110, h: 60, g: [
|
||||
box(), ['r', 0.08, 0.14, 0.84, 0.72],
|
||||
] },
|
||||
{ id: 'table_dining', group: 'furniture', w: 140, h: 80, g: [
|
||||
{ id: 'table_dining', group: 'furniture', category: 'dining_table', w: 140, h: 80, g: [
|
||||
box(), ['r', 0.06, 0.11, 0.88, 0.78],
|
||||
] },
|
||||
{ id: 'table_round', group: 'furniture', w: 120, h: 120, g: [
|
||||
{ id: 'table_round', group: 'furniture', category: 'dining_table', w: 120, h: 120, g: [
|
||||
['e', 0.5, 0.5, 0.5, 0.5], ['e', 0.5, 0.5, 0.41, 0.41],
|
||||
] },
|
||||
{ id: 'chair', group: 'furniture', w: 45, h: 45, g: [
|
||||
{ id: 'chair', group: 'furniture', category: 'chair', w: 45, h: 45, g: [
|
||||
['r', 0, 0, 1, 0.18], // the back
|
||||
['r', 0.06, 0.18, 0.88, 0.8], // the seat
|
||||
] },
|
||||
{ id: 'desk', group: 'furniture', w: 120, h: 60, g: [
|
||||
{ id: 'desk', group: 'furniture', category: 'work_table', w: 120, h: 60, g: [
|
||||
box(),
|
||||
['r', 0.63, 0.07, 0.31, 0.86], // the drawer pedestal
|
||||
['l', 0.63, 0.5, 0.94, 0.5],
|
||||
] },
|
||||
{ id: 'bed_double', group: 'furniture', w: 160, h: 200, g: [
|
||||
{ id: 'bed_double', group: 'furniture', category: 'bed', w: 160, h: 200, g: [
|
||||
box(),
|
||||
['r', 0, 0, 1, 0.07], // the headboard, i.e. the back
|
||||
['r', 0.06, 0.1, 0.4, 0.15], // two pillows
|
||||
['r', 0.54, 0.1, 0.4, 0.15],
|
||||
['l', 0, 0.33, 1, 0.33], // the turned-down blanket
|
||||
] },
|
||||
{ id: 'bed_single', group: 'furniture', w: 90, h: 200, g: [
|
||||
{ id: 'bed_single', group: 'furniture', category: 'bed', w: 90, h: 200, g: [
|
||||
box(),
|
||||
['r', 0, 0, 1, 0.07],
|
||||
['r', 0.15, 0.1, 0.7, 0.15],
|
||||
['l', 0, 0.33, 1, 0.33],
|
||||
] },
|
||||
{ id: 'nightstand', group: 'furniture', w: 45, h: 40, g: [
|
||||
{ id: 'nightstand', group: 'furniture', category: 'nightstand', w: 45, h: 40, g: [
|
||||
box(), ['r', 0.12, 0.14, 0.76, 0.33], ['r', 0.12, 0.53, 0.76, 0.33],
|
||||
] },
|
||||
{ id: 'wardrobe', group: 'furniture', w: 100, h: 60, g: [
|
||||
{ id: 'wardrobe', group: 'furniture', category: 'wardrobe', w: 100, h: 60, g: [
|
||||
box(),
|
||||
['l', 0, 0.72, 1, 0.72], // the hanging rail
|
||||
['l', 0.5, 0.72, 0.5, 1], // the doors meet here
|
||||
] },
|
||||
{ id: 'bookshelf', group: 'furniture', w: 80, h: 30, g: [
|
||||
{ id: 'bookshelf', group: 'furniture', category: 'wardrobe', w: 80, h: 30, g: [
|
||||
box(), ['l', 0.34, 0, 0.34, 1], ['l', 0.67, 0, 0.67, 1],
|
||||
] },
|
||||
|
||||
// ------------------------------- техника -------------------------------
|
||||
{ id: 'fridge', group: 'appliance', w: 60, h: 65, g: [
|
||||
{ id: 'fridge', group: 'appliance', category: 'fridge', w: 60, h: 65, g: [
|
||||
box(),
|
||||
['l', 0, 0.36, 1, 0.36], // freezer / fridge
|
||||
['l', 0.83, 0.44, 0.83, 0.64], // the handle
|
||||
] },
|
||||
{ id: 'stove', group: 'appliance', w: 60, h: 60, g: [
|
||||
{ id: 'stove', group: 'appliance', category: 'cooktop', w: 60, h: 60, g: [
|
||||
box(),
|
||||
['e', 0.29, 0.31, 0.15, 0.15], ['e', 0.71, 0.31, 0.15, 0.15],
|
||||
['e', 0.29, 0.71, 0.15, 0.15], ['e', 0.71, 0.71, 0.15, 0.15],
|
||||
] },
|
||||
{ id: 'dishwasher', group: 'appliance', w: 60, h: 60, g: [
|
||||
{ id: 'dishwasher', group: 'appliance', category: 'dishwasher', w: 60, h: 60, g: [
|
||||
box(),
|
||||
['r', 0.1, 0.12, 0.8, 0.76],
|
||||
['e', 0.5, 0.5, 0.27, 0.27], ['e', 0.5, 0.5, 0.13, 0.13], // plates
|
||||
] },
|
||||
{ id: 'washer', group: 'appliance', w: 60, h: 60, g: [
|
||||
{ id: 'washer', group: 'appliance', category: 'washer', w: 60, h: 60, g: [
|
||||
box(),
|
||||
['l', 0.08, 0.17, 0.92, 0.17], // the control panel
|
||||
['e', 0.5, 0.57, 0.3, 0.3], ['e', 0.5, 0.57, 0.14, 0.14], // the drum
|
||||
] },
|
||||
{ id: 'dryer', group: 'appliance', w: 60, h: 60, g: [
|
||||
{ id: 'dryer', group: 'appliance', category: 'dryer', w: 60, h: 60, g: [
|
||||
box(),
|
||||
['l', 0.08, 0.17, 0.92, 0.17],
|
||||
['e', 0.5, 0.57, 0.3, 0.3],
|
||||
['p', 0.36, 0.5, 0.5, 0.64, 0.64, 0.5], // the chevron that is not a drum
|
||||
] },
|
||||
{ id: 'tv', group: 'appliance', w: 120, h: 30, g: [
|
||||
{ id: 'tv', group: 'appliance', category: 'tv', w: 120, h: 30, g: [
|
||||
['r', 0, 0, 1, 0.42], // the screen, seen edge-on
|
||||
['l', 0.5, 0.42, 0.5, 0.72],
|
||||
['l', 0.3, 0.72, 0.7, 0.72], // the stand
|
||||
] },
|
||||
{ id: 'ac', group: 'appliance', w: 90, h: 25, g: [
|
||||
{ id: 'ac', group: 'appliance', category: 'air_conditioner', w: 90, h: 25, g: [
|
||||
box(), ['l', 0.05, 0.55, 0.95, 0.55], ['l', 0.05, 0.79, 0.95, 0.79],
|
||||
] },
|
||||
{ id: 'water_heater', group: 'appliance', w: 45, h: 45, g: [
|
||||
{ id: 'water_heater', group: 'appliance', category: 'boiler', w: 45, h: 45, g: [
|
||||
['e', 0.5, 0.5, 0.5, 0.5], ['e', 0.5, 0.5, 0.31, 0.31],
|
||||
] },
|
||||
|
||||
// ----------------------------- сантехника ------------------------------
|
||||
{ id: 'toilet', group: 'sanitary', w: 40, h: 70, g: [
|
||||
{ id: 'toilet', group: 'sanitary', category: 'toilet', w: 40, h: 70, g: [
|
||||
['r', 0.06, 0, 0.88, 0.2], // the cistern, against the wall
|
||||
['e', 0.5, 0.58, 0.37, 0.35], // the bowl
|
||||
['e', 0.5, 0.58, 0.22, 0.2],
|
||||
] },
|
||||
{ id: 'bathtub', group: 'sanitary', w: 170, h: 75, g: [
|
||||
{ id: 'bathtub', group: 'sanitary', category: 'bathtub', w: 170, h: 75, g: [
|
||||
box(),
|
||||
['r', 0.05, 0.11, 0.77, 0.78],
|
||||
['e', 0.89, 0.5, 0.045, 0.1], // the drain end
|
||||
] },
|
||||
{ id: 'shower', group: 'sanitary', w: 90, h: 90, g: [
|
||||
{ id: 'shower', group: 'sanitary', category: 'shower', w: 90, h: 90, g: [
|
||||
box(),
|
||||
['l', 0, 0, 1, 1], ['l', 1, 0, 0, 1], // the tray, as every plan draws it
|
||||
['e', 0.5, 0.5, 0.08, 0.08],
|
||||
] },
|
||||
{ id: 'sink', group: 'sanitary', w: 60, h: 45, g: [
|
||||
{ id: 'sink', group: 'sanitary', category: 'sink', w: 60, h: 45, g: [
|
||||
box(),
|
||||
['e', 0.5, 0.6, 0.34, 0.3],
|
||||
['e', 0.5, 0.15, 0.07, 0.07], // the tap
|
||||
] },
|
||||
{ id: 'kitchen_sink', group: 'sanitary', w: 80, h: 60, g: [
|
||||
{ id: 'kitchen_sink', group: 'sanitary', category: 'kitchen_sink', w: 80, h: 60, g: [
|
||||
box(),
|
||||
['r', 0.06, 0.24, 0.44, 0.64],
|
||||
['r', 0.54, 0.24, 0.4, 0.64],
|
||||
['e', 0.5, 0.12, 0.06, 0.06],
|
||||
] },
|
||||
{ id: 'bidet', group: 'sanitary', w: 40, h: 55, g: [
|
||||
{ id: 'bidet', group: 'sanitary', category: 'bidet', w: 40, h: 55, g: [
|
||||
['e', 0.5, 0.5, 0.44, 0.5], ['e', 0.5, 0.5, 0.26, 0.3],
|
||||
] },
|
||||
|
||||
// ------------------------------- прочее --------------------------------
|
||||
{ id: 'stairs', group: 'other', w: 100, h: 280, g: [
|
||||
{ id: 'stairs', group: 'other', category: 'stairs', w: 100, h: 280, g: [
|
||||
box(),
|
||||
['l', 0, 0.111, 1, 0.111], ['l', 0, 0.222, 1, 0.222], ['l', 0, 0.333, 1, 0.333],
|
||||
['l', 0, 0.444, 1, 0.444], ['l', 0, 0.556, 1, 0.556], ['l', 0, 0.667, 1, 0.667],
|
||||
@@ -205,21 +218,40 @@ export const FURNITURE: FurnitureSymbol[] = [
|
||||
['l', 0.5, 0.93, 0.5, 0.06], // the "up" arrow
|
||||
['p', 0.38, 0.16, 0.5, 0.06, 0.62, 0.16],
|
||||
] },
|
||||
{ id: 'fireplace', group: 'other', w: 120, h: 40, g: [
|
||||
{ id: 'fireplace', group: 'other', category: 'fireplace', w: 120, h: 40, g: [
|
||||
box(), ['p', 0.22, 1, 0.22, 0.42, 0.78, 0.42, 0.78, 1],
|
||||
] },
|
||||
{ id: 'plant', group: 'other', w: 40, h: 40, g: [
|
||||
{ id: 'plant', group: 'other', category: 'plant', w: 40, h: 40, g: [
|
||||
['e', 0.5, 0.5, 0.22, 0.22],
|
||||
['l', 0.5, 0.28, 0.5, 0.02], ['l', 0.5, 0.72, 0.5, 0.98],
|
||||
['l', 0.28, 0.5, 0.02, 0.5], ['l', 0.72, 0.5, 0.98, 0.5],
|
||||
['l', 0.34, 0.34, 0.13, 0.13], ['l', 0.66, 0.66, 0.87, 0.87],
|
||||
['l', 0.66, 0.34, 0.87, 0.13], ['l', 0.34, 0.66, 0.13, 0.87],
|
||||
] },
|
||||
{ id: 'rug', group: 'other', w: 200, h: 140, g: [
|
||||
{ id: 'rug', group: 'other', category: 'rug', w: 200, h: 140, g: [
|
||||
box(), ['r', 0.06, 0.09, 0.88, 0.82],
|
||||
] },
|
||||
];
|
||||
|
||||
const RETAINED_IDS = new Set([
|
||||
'fridge', 'dishwasher', 'washer', 'dryer', 'ac', 'water_heater',
|
||||
'shower', 'sink', 'stairs', 'fireplace', 'plant', 'rug',
|
||||
]);
|
||||
|
||||
/** Complete public library: 44 designer symbols plus the 12 useful existing
|
||||
* symbols for which the pack intentionally has no top-view replacement. */
|
||||
export const FURNITURE: FurnitureSymbol[] = [
|
||||
...GENERATED_FURNITURE_PLAN.map((symbol) => ({
|
||||
id: symbol.id,
|
||||
group: symbol.group as FurnitureGroup,
|
||||
category: symbol.category,
|
||||
w: symbol.w,
|
||||
h: symbol.h,
|
||||
art: symbol.art,
|
||||
})),
|
||||
...LEGACY_FURNITURE.filter((symbol) => RETAINED_IDS.has(symbol.id)),
|
||||
];
|
||||
|
||||
const BY_ID = new Map(FURNITURE.map((s) => [s.id, s]));
|
||||
|
||||
/** The symbol with this id, or null. An unknown id is DATA, not a crash: a
|
||||
@@ -295,9 +327,8 @@ const num = (v: number): string => {
|
||||
* takes a single stroke, a single pointer target and a single `data-symbol`,
|
||||
* and the decor layer's DOM does not grow by ten nodes per sofa.
|
||||
*/
|
||||
export function furniturePathD(id: string, w: number, h: number): string {
|
||||
const s = furnitureSymbol(id);
|
||||
if (!s || !(w > 0) || !(h > 0)) return '';
|
||||
function primitivePathD(s: FurnitureSymbol, w: number, h: number): string {
|
||||
if (!s.g || !(w > 0) || !(h > 0)) return '';
|
||||
const X = (v: number) => num(v * w);
|
||||
const Y = (v: number) => num(v * h);
|
||||
const out: string[] = [];
|
||||
@@ -325,6 +356,25 @@ export function furniturePathD(id: string, w: number, h: number): string {
|
||||
return out.join('');
|
||||
}
|
||||
|
||||
/** Native one-path artwork and its coordinate box. The renderer scales this
|
||||
* box non-uniformly to the user's stored dimensions and keeps the decor stroke
|
||||
* physical with `vector-effect=non-scaling-stroke`. */
|
||||
export function furnitureGraphic(id: string): FurnitureGraphic | null {
|
||||
const symbol = furnitureSymbol(id);
|
||||
if (!symbol) return null;
|
||||
if (symbol.art) return symbol.art;
|
||||
const d = primitivePathD(symbol, 1, 1);
|
||||
return d ? { d, viewW: 1, viewH: 1 } : null;
|
||||
}
|
||||
|
||||
/** Compatibility helper for callers that only need a path string. Designer
|
||||
* artwork remains in its native viewBox; use `furnitureGraphic` when sizing it. */
|
||||
export function furniturePathD(id: string, w: number, h: number): string {
|
||||
const symbol = furnitureSymbol(id);
|
||||
if (!symbol || !(w > 0) || !(h > 0)) return '';
|
||||
return symbol.art?.d || primitivePathD(symbol, w, h);
|
||||
}
|
||||
|
||||
// ------------------------------ the wall magnet -----------------------------
|
||||
|
||||
/** How far from a wall the magnet still reaches, in GRID CELLS. Six cells is
|
||||
|
||||
+16
-17
@@ -61,7 +61,7 @@ import {
|
||||
import { dayCycleStageVars, renderDayCycleEnvironment } from './day-cycle-render';
|
||||
import {
|
||||
FURNITURE_GROUPS, furnitureOfGroup, furnitureSymbol, furnitureDefaultCm,
|
||||
furniturePathD, furnitureCorners, snapFurnitureToWall,
|
||||
furnitureGraphic, furnitureCorners, snapFurnitureToWall,
|
||||
cmToNorm, clampFurnSize, clampFurnCm, FURN_WALL_CELLS, type FurnitureGroup,
|
||||
} from './furniture';
|
||||
import {
|
||||
@@ -1033,6 +1033,8 @@ export class HouseplanCard extends LitElement {
|
||||
* a unit system is a display setting, never a stored one.
|
||||
*/
|
||||
private _furnPalette: { symbol: string; w: number; h: number } | null = null;
|
||||
/** Category currently open in the two-level furniture palette. */
|
||||
private _furnCategory: string | null = null;
|
||||
/** The selected shape's own unrotated frame (text is measured from SVG). */
|
||||
private _dtBox: { id: string; x: number; y: number; w: number; h: number } | null = null;
|
||||
/**
|
||||
@@ -2426,6 +2428,7 @@ export class HouseplanCard extends LitElement {
|
||||
_decorShapeDialog: { state: true },
|
||||
_backdropDialog: { state: true },
|
||||
_furnPalette: { state: true },
|
||||
_furnCategory: { state: true },
|
||||
_bdDrag: { state: true },
|
||||
_dtBox: { state: true },
|
||||
_dtDrag: { state: true },
|
||||
@@ -2777,6 +2780,7 @@ export class HouseplanCard extends LitElement {
|
||||
// to Select in one step, regardless of whether a symbol was armed.
|
||||
else if (this._decorTool === 'furniture') {
|
||||
this._furnPalette = null;
|
||||
this._furnCategory = null;
|
||||
this._decorTool = 'select';
|
||||
}
|
||||
else if (this._decorSel) this._decorSel = null;
|
||||
@@ -7823,13 +7827,7 @@ export class HouseplanCard extends LitElement {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The palette: every symbol of the library, grouped, each drawn with the
|
||||
* very same `furniturePathD` the plan uses — so what the user picks is what
|
||||
* the user gets, at any size, and there is no second set of preview assets
|
||||
* to keep in step. Under it the two size fields, prefilled with the chosen
|
||||
* symbol's real size and shown in metres or feet by the HA unit system.
|
||||
*/
|
||||
/** The lazy editor runtime owns the generated two-level furniture palette. */
|
||||
private _renderFurnPalette(): TemplateResult {
|
||||
return this._editorRuntimeOrThrow()._renderFurnPalette();
|
||||
}
|
||||
@@ -8068,24 +8066,25 @@ export class HouseplanCard extends LitElement {
|
||||
transform=${ang ? `rotate(${ang} ${cx} ${cy})` : nothing} @pointerdown=${down}></ellipse>` : nothing}`;
|
||||
}
|
||||
if (sh.kind === 'furniture') {
|
||||
// One path per piece, generated at the shape's REAL size, so the
|
||||
// stroke is an ordinary stroke-width in render units like every other
|
||||
// decor shape — no non-uniform scale to fight (docs/FURNITURE.md §2).
|
||||
// One path per piece. Designer art keeps its native viewBox and is
|
||||
// scaled to the user's stored box; vector-effect keeps the configured
|
||||
// decor stroke physical under non-uniform resizing.
|
||||
// An unknown symbol renders as nothing: a plan from a newer card must
|
||||
// open in an older one, not break it.
|
||||
const W2 = sh.w * W, H2 = sh.h * H;
|
||||
const d = furniturePathD(sh.symbol, W2, H2);
|
||||
if (!d) return nothing;
|
||||
const art = furnitureGraphic(sh.symbol);
|
||||
if (!art) return nothing;
|
||||
const ang = Number(sh.angle) || 0;
|
||||
const cx = sh.x * W + W2 / 2, cy = sh.y * H + H2 / 2;
|
||||
const tr = `${ang ? `rotate(${ang} ${cx} ${cy}) ` : ''}translate(${sh.x * W} ${sh.y * H})`;
|
||||
const tr = `${ang ? `rotate(${ang} ${cx} ${cy}) ` : ''}translate(${sh.x * W} ${sh.y * H}) scale(${W2 / art.viewW} ${H2 / art.viewH})`;
|
||||
return svg`<path class="${cls} dfurn" data-hp="decor" data-id="${sh.id}"
|
||||
data-kind="${sh.kind}" data-symbol="${sh.symbol}" d="${d}" transform=${tr}
|
||||
data-kind="${sh.kind}" data-symbol="${sh.symbol}" d="${art.d}" transform=${tr}
|
||||
stroke="${style.color}" stroke-opacity="${style.opacity}" stroke-width="${strokeWidth}" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke"
|
||||
@pointerdown=${down} @dblclick=${dbl}></path>
|
||||
${erasing ? svg`<path class="dshape derasehit" data-hp="decor" data-id="${sh.id}"
|
||||
data-kind="${sh.kind}" data-symbol="${sh.symbol}" d="${d}" transform=${tr}
|
||||
data-kind="${sh.kind}" data-symbol="${sh.symbol}" d="${art.d}" transform=${tr}
|
||||
vector-effect="non-scaling-stroke"
|
||||
@pointerdown=${down}></path>` : nothing}`;
|
||||
}
|
||||
if (sh.kind === 'text') {
|
||||
|
||||
@@ -63,10 +63,11 @@ import {
|
||||
} from './sun';
|
||||
import { dayCycleStageVars, renderDayCycleEnvironment } from './day-cycle-render';
|
||||
import {
|
||||
FURNITURE_GROUPS, furnitureOfGroup, furnitureSymbol, furnitureDefaultCm,
|
||||
furniturePathD, furnitureCorners, snapFurnitureToWall,
|
||||
FURNITURE_GROUPS, furnitureOfGroup, furnitureDefaultCm,
|
||||
furnitureGraphic, furnitureCorners, snapFurnitureToWall,
|
||||
cmToNorm, clampFurnSize, clampFurnCm, FURN_WALL_CELLS, type FurnitureGroup,
|
||||
} from './furniture';
|
||||
import { GENERATED_FURNITURE_MENU } from './furniture-menu-art.generated';
|
||||
import {
|
||||
degradeWalls, rekeyWallsAfterMoveChecked, wallRecordCarrierViolations,
|
||||
setWallThickness, setWallThicknessForRoom, cmToField, wallCmToUnits,
|
||||
@@ -915,6 +916,7 @@ export interface HouseplanEditorHostPort {
|
||||
_freeAreas: any[];
|
||||
_fullRegistryHass: any;
|
||||
_furnPalette: { symbol: string; w: number; h: number; } | null;
|
||||
_furnCategory: string | null;
|
||||
_furnWallReach: number;
|
||||
_furnWalls: number[][];
|
||||
_gearPtCache: WeakMap<number[][], number[]>;
|
||||
@@ -4661,6 +4663,7 @@ public _furnPlace(raw: number[], free = false): void {
|
||||
// …and the editor goes back to the tool that can move what was just placed
|
||||
this.host._decorTool = 'select';
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = null;
|
||||
this._recordGeometry(this.host._t('history.decor_add'), before);
|
||||
this._saveConfig();
|
||||
this.host.requestUpdate();
|
||||
@@ -4726,38 +4729,74 @@ public _decorApplyBox(id: string, box: { x: number; y: number; w: number; h: num
|
||||
|
||||
public _renderFurnPalette(): TemplateResult {
|
||||
const pal = this.host._furnPalette;
|
||||
const categoryId = this.host._furnCategory;
|
||||
const allSymbols = FURNITURE_GROUPS.flatMap((group) => furnitureOfGroup(group));
|
||||
const categories = GENERATED_FURNITURE_MENU.filter((item) =>
|
||||
allSymbols.some((symbol) => symbol.category === item.id));
|
||||
const category = categories.find((item) => item.id === categoryId) || null;
|
||||
const variants = category
|
||||
? allSymbols.filter((symbol) => symbol.category === category.id)
|
||||
: [];
|
||||
// the unit the fields are read in — the same two words the glow radius
|
||||
// already uses, because a plan has one unit system, not one per control
|
||||
const unit = this.host._t(this.host._imperial ? 'gs.unit_ft' : 'gs.unit_m');
|
||||
const preview = (id: string): TemplateResult => {
|
||||
const s = furnitureSymbol(id)!;
|
||||
// fit the symbol into a 40×40 box keeping its real proportions, so a
|
||||
// sofa reads as a sofa and a toilet does not become a square
|
||||
const k = 36 / Math.max(s.w, s.h);
|
||||
const w = s.w * k, h = s.h * k;
|
||||
return svg`<svg class="furnprev" viewBox="0 0 40 40" aria-hidden="true"><g
|
||||
transform="translate(${(40 - w) / 2} ${(40 - h) / 2})"><path
|
||||
d=${furniturePathD(id, w, h)} fill="none" stroke="currentColor"
|
||||
stroke-width="1.2" stroke-linejoin="round"></path></g></svg>` as unknown as TemplateResult;
|
||||
const art = furnitureGraphic(id);
|
||||
if (!art) return svg`<svg class="furnprev" aria-hidden="true"></svg>` as unknown as TemplateResult;
|
||||
return svg`<svg class="furnprev" viewBox=${`0 0 ${art.viewW} ${art.viewH}`}
|
||||
preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path
|
||||
d=${art.d} fill="none" stroke="currentColor" stroke-width="1.2"
|
||||
vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round"></path></svg>` as unknown as TemplateResult;
|
||||
};
|
||||
const categoryPreview = (item: (typeof GENERATED_FURNITURE_MENU)[number]): TemplateResult => {
|
||||
return svg`<svg class="furnprev furncatprev" viewBox=${`0 0 ${item.art.viewW} ${item.art.viewH}`}
|
||||
preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path
|
||||
d=${item.art.d} fill="none" stroke="currentColor" stroke-width="1.5"
|
||||
vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round"></path></svg>` as unknown as TemplateResult;
|
||||
};
|
||||
return html`<div class="furnpalette" @pointerdown=${(e: Event) => e.stopPropagation()}>
|
||||
<div class="furnhd">
|
||||
<ha-icon icon="mdi:sofa-outline"></ha-icon>${this.host._t('furn.title')}
|
||||
<span class="spacer"></span>
|
||||
<button class="btn furnclose" title=${this.host._t('btn.close')}
|
||||
@click=${() => { this.host._furnPalette = null; this.host._decorTool = 'select'; }}>
|
||||
@click=${() => {
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = null;
|
||||
this.host._decorTool = 'select';
|
||||
}}>
|
||||
<ha-icon icon="mdi:close"></ha-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="furnbody">
|
||||
${FURNITURE_GROUPS.map((g) => html`
|
||||
${category ? html`
|
||||
<button class="btn ghost furnback" @click=${() => {
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = null;
|
||||
}}>
|
||||
<ha-icon icon="mdi:arrow-left"></ha-icon>${this.host._t('furn.back_to_categories')}
|
||||
</button>
|
||||
<div class="furngroup" data-category=${category.id}>
|
||||
${this.host._t(`furn.cat_${category.id}` as I18nKey)}
|
||||
</div>
|
||||
<div class="furnrow furnvariants">
|
||||
${variants.map((symbol) => html`<button
|
||||
class="furnitem ${pal?.symbol === symbol.id ? 'on' : ''}" data-symbol=${symbol.id}
|
||||
title=${this.host._t(`furn.sym_${symbol.id}` as any)}
|
||||
@click=${() => this._furnPick(symbol.id)}>
|
||||
${preview(symbol.id)}<span>${this.host._t(`furn.sym_${symbol.id}` as any)}</span>
|
||||
</button>`)}
|
||||
</div>
|
||||
` : FURNITURE_GROUPS.map((g) => html`
|
||||
<div class="furngroup" data-group=${g}>${this.host._t(`furn.group_${g}` as any)}</div>
|
||||
<div class="furnrow">
|
||||
${furnitureOfGroup(g as FurnitureGroup).map((s) => html`<button
|
||||
class="furnitem ${pal?.symbol === s.id ? 'on' : ''}" data-symbol=${s.id}
|
||||
title=${this.host._t(`furn.sym_${s.id}` as any)}
|
||||
@click=${() => this._furnPick(s.id)}>
|
||||
${preview(s.id)}<span>${this.host._t(`furn.sym_${s.id}` as any)}</span>
|
||||
<div class="furnrow furncategories">
|
||||
${categories.filter((item) => item.group === g).map((item) => html`<button
|
||||
class="furnitem furncategory" data-category=${item.id}
|
||||
title=${this.host._t(`furn.cat_${item.id}` as I18nKey)}
|
||||
@click=${() => {
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = item.id;
|
||||
}}>
|
||||
${categoryPreview(item)}<span>${this.host._t(`furn.cat_${item.id}` as I18nKey)}</span>
|
||||
</button>`)}
|
||||
</div>`)}
|
||||
</div>
|
||||
@@ -5074,6 +5113,7 @@ public _renderDecorSecondary(): EditorSecondaryModel | null {
|
||||
dismiss: () => {
|
||||
if (this.host._decorTool !== 'furniture') return;
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = null;
|
||||
this.host._decorTool = 'select';
|
||||
this.host.requestUpdate();
|
||||
},
|
||||
@@ -5195,6 +5235,7 @@ public _renderDecorBar(): TemplateResult {
|
||||
@click=${() => {
|
||||
if (t === 'furniture' && this.host._decorTool === 'furniture') {
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = null;
|
||||
this.host._decorTool = 'select';
|
||||
return;
|
||||
}
|
||||
@@ -5203,7 +5244,12 @@ public _renderDecorBar(): TemplateResult {
|
||||
this.host._decorDraft = null;
|
||||
// the palette belongs to its tool and to nothing else: leaving the
|
||||
// tool disarms whatever was chosen, so no later click can stamp it
|
||||
if (t !== 'furniture') this.host._furnPalette = null;
|
||||
if (t !== 'furniture') {
|
||||
this.host._furnPalette = null;
|
||||
this.host._furnCategory = null;
|
||||
} else {
|
||||
this.host._furnCategory = null;
|
||||
}
|
||||
}}
|
||||
title=${this.host._t(k)}>
|
||||
<ha-icon icon=${ic}></ha-icon><span class="ml">${this.host._t(k)}</span>
|
||||
@@ -5335,8 +5381,13 @@ public _renderDecorShapeDialog(): TemplateResult {
|
||||
@change=${(e: Event) => (this.host._decorShapeDialog = {
|
||||
...d, symbol: (e.target as HTMLSelectElement).value,
|
||||
})}>
|
||||
${FURNITURE_GROUPS.map((group) => html`<optgroup label=${this.host._t(`furn.group_${group}` as any)}>
|
||||
${furnitureOfGroup(group).map((symbol) => html`<option value=${symbol.id}
|
||||
${GENERATED_FURNITURE_MENU.map((category) => ({
|
||||
category,
|
||||
symbols: furnitureOfGroup(category.group as FurnitureGroup)
|
||||
.filter((symbol) => symbol.category === category.id),
|
||||
})).filter((entry) => entry.symbols.length).map(({ category, symbols }) => html`
|
||||
<optgroup label=${`${this.host._t(`furn.group_${category.group}` as I18nKey)} · ${this.host._t(`furn.cat_${category.id}` as I18nKey)}`}>
|
||||
${symbols.map((symbol) => html`<option value=${symbol.id}
|
||||
?selected=${symbol.id === d.symbol}>
|
||||
${this.host._t(`furn.sym_${symbol.id}` as any)}
|
||||
</option>`)}
|
||||
|
||||
+69
-13
@@ -900,34 +900,90 @@
|
||||
"furn.group_other": "Sonstiges",
|
||||
"furn.width": "Breite",
|
||||
"furn.depth": "Tiefe",
|
||||
"furn.pick_hint": "Wählen Sie ein Symbol aus und klicken Sie dann auf den Plan.",
|
||||
"furn.back_to_categories": "Alle Kategorien",
|
||||
"furn.pick_hint": "Wählen Sie eine Kategorie und dann ein Symbol.",
|
||||
"furn.place_hint": "Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.",
|
||||
"furn.sym_sofa": "Sofa",
|
||||
"furn.sym_armchair": "Sessel",
|
||||
"furn.sym_coffee_table": "Couchtisch",
|
||||
"furn.sym_table_dining": "Esstisch",
|
||||
"furn.cat_air_conditioner": "Klimaanlagen",
|
||||
"furn.cat_armchair": "Sessel",
|
||||
"furn.cat_bathtub": "Badewannen",
|
||||
"furn.cat_bed": "Betten",
|
||||
"furn.cat_bidet": "Bidets",
|
||||
"furn.cat_boiler": "Warmwasserbereiter",
|
||||
"furn.cat_chair": "Stühle",
|
||||
"furn.cat_coffee_table": "Couchtische",
|
||||
"furn.cat_cooktop": "Kochfelder",
|
||||
"furn.cat_dining_table": "Esstische",
|
||||
"furn.cat_dishwasher": "Geschirrspüler",
|
||||
"furn.cat_dryer": "Wäschetrockner",
|
||||
"furn.cat_fireplace": "Kamine",
|
||||
"furn.cat_fridge": "Kühlschränke",
|
||||
"furn.cat_kitchen_cabinet": "Küchenschränke",
|
||||
"furn.cat_kitchen_sink": "Küchenspülen",
|
||||
"furn.cat_nightstand": "Schränke",
|
||||
"furn.cat_plant": "Pflanzen",
|
||||
"furn.cat_rug": "Teppiche",
|
||||
"furn.cat_shelving": "Regale",
|
||||
"furn.cat_shower": "Duschen",
|
||||
"furn.cat_sink": "Waschbecken",
|
||||
"furn.cat_sofa": "Sofas",
|
||||
"furn.cat_stairs": "Treppen",
|
||||
"furn.cat_toilet": "Toiletten",
|
||||
"furn.cat_tv": "Fernseher",
|
||||
"furn.cat_wardrobe": "Kleiderschränke",
|
||||
"furn.cat_washer": "Waschmaschinen",
|
||||
"furn.cat_work_table": "Schreibtische",
|
||||
"furn.sym_sofa": "Zweisitzer-Sofa",
|
||||
"furn.sym_sofa_three_seat": "Dreisitzer-Sofa",
|
||||
"furn.sym_sofa_corner_right": "Ecksofa rechts",
|
||||
"furn.sym_armchair": "Polstersessel",
|
||||
"furn.sym_armchair_office": "Bürostuhl",
|
||||
"furn.sym_coffee_table": "Rechteckiger Couchtisch",
|
||||
"furn.sym_coffee_table_round": "Runder Couchtisch",
|
||||
"furn.sym_coffee_table_oval": "Ovaler Couchtisch",
|
||||
"furn.sym_coffee_table_rounded": "Abgerundeter Couchtisch",
|
||||
"furn.sym_table_dining": "Rechteckiger Esstisch",
|
||||
"furn.sym_table_round": "Rundtisch",
|
||||
"furn.sym_table_dining_oval": "Ovaler Esstisch",
|
||||
"furn.sym_table_dining_rounded": "Abgerundeter Esstisch",
|
||||
"furn.sym_chair": "Stuhl",
|
||||
"furn.sym_desk": "Schreibtisch",
|
||||
"furn.sym_chair_bar": "Barhocker",
|
||||
"furn.sym_desk": "Rechteckiger Schreibtisch",
|
||||
"furn.sym_desk_corner": "Eckschreibtisch",
|
||||
"furn.sym_bed_double": "Doppelbett",
|
||||
"furn.sym_bed_single": "Einzelbett",
|
||||
"furn.sym_nightstand": "Nachttisch",
|
||||
"furn.sym_cabinet_tv": "TV-Schrank",
|
||||
"furn.sym_cabinet_shoe": "Schuhschrank",
|
||||
"furn.sym_cabinet_sink": "Waschtischunterschrank",
|
||||
"furn.sym_wardrobe": "Kleiderschrank",
|
||||
"furn.sym_bookshelf": "Bücherregal",
|
||||
"furn.sym_bookshelf": "Bücherschrank",
|
||||
"furn.sym_wall_unit": "Schrankwand",
|
||||
"furn.sym_kitchen_floor": "Küchen-Unterschrank",
|
||||
"furn.sym_kitchen_floor_corner": "Küchen-Eckunterschrank",
|
||||
"furn.sym_kitchen_wall": "Küchen-Hängeschrank",
|
||||
"furn.sym_kitchen_wall_corner": "Küchen-Eckhängeschrank",
|
||||
"furn.sym_shelf_floor": "Standregal",
|
||||
"furn.sym_shelf_wall": "Wandregal",
|
||||
"furn.sym_fridge": "Kühlschrank",
|
||||
"furn.sym_stove": "Herd",
|
||||
"furn.sym_stove": "Vierflammiges Kochfeld",
|
||||
"furn.sym_cooktop_two": "Zweiflammiges Kochfeld",
|
||||
"furn.sym_dishwasher": "Geschirrspüler",
|
||||
"furn.sym_washer": "Waschmaschine",
|
||||
"furn.sym_dryer": "Wäschetrockner",
|
||||
"furn.sym_tv": "Fernseher",
|
||||
"furn.sym_tv": "Fernseher mit Standfuß",
|
||||
"furn.sym_tv_wall": "Wandfernseher",
|
||||
"furn.sym_ac": "Klimaanlage",
|
||||
"furn.sym_water_heater": "Wassererhitzer",
|
||||
"furn.sym_toilet": "Toilette",
|
||||
"furn.sym_bathtub": "Badewanne",
|
||||
"furn.sym_toilet": "Stand-WC",
|
||||
"furn.sym_toilet_built_in": "Wand-WC",
|
||||
"furn.sym_bathtub": "Rechteckige Badewanne",
|
||||
"furn.sym_bathtub_corner": "Eckbadewanne",
|
||||
"furn.sym_shower": "Dusche",
|
||||
"furn.sym_sink": "Waschbecken",
|
||||
"furn.sym_kitchen_sink": "Küchenspüle",
|
||||
"furn.sym_bidet": "Bidet",
|
||||
"furn.sym_kitchen_sink": "Einzel-Küchenspüle",
|
||||
"furn.sym_kitchen_sink_double": "Doppel-Küchenspüle",
|
||||
"furn.sym_bidet": "Stand-Bidet",
|
||||
"furn.sym_bidet_built_in": "Wand-Bidet",
|
||||
"furn.sym_stairs": "Treppe",
|
||||
"furn.sym_fireplace": "Kamin",
|
||||
"furn.sym_plant": "Pflanze",
|
||||
|
||||
+70
-14
@@ -900,34 +900,90 @@
|
||||
"furn.group_other": "Other",
|
||||
"furn.width": "Width",
|
||||
"furn.depth": "Depth",
|
||||
"furn.pick_hint": "Pick a symbol, then click on the plan.",
|
||||
"furn.back_to_categories": "All categories",
|
||||
"furn.pick_hint": "Choose a category, then a symbol.",
|
||||
"furn.place_hint": "Click on the plan — the piece lands against the nearest wall. Shift places it free.",
|
||||
"furn.sym_sofa": "Sofa",
|
||||
"furn.sym_armchair": "Armchair",
|
||||
"furn.sym_coffee_table": "Coffee table",
|
||||
"furn.sym_table_dining": "Dining table",
|
||||
"furn.cat_air_conditioner": "Air conditioners",
|
||||
"furn.cat_armchair": "Armchairs",
|
||||
"furn.cat_bathtub": "Bathtubs",
|
||||
"furn.cat_bed": "Beds",
|
||||
"furn.cat_bidet": "Bidets",
|
||||
"furn.cat_boiler": "Water heaters",
|
||||
"furn.cat_chair": "Chairs",
|
||||
"furn.cat_coffee_table": "Coffee tables",
|
||||
"furn.cat_cooktop": "Cooktops",
|
||||
"furn.cat_dining_table": "Dining tables",
|
||||
"furn.cat_dishwasher": "Dishwashers",
|
||||
"furn.cat_dryer": "Dryers",
|
||||
"furn.cat_fireplace": "Fireplaces",
|
||||
"furn.cat_fridge": "Refrigerators",
|
||||
"furn.cat_kitchen_cabinet": "Kitchen cabinets",
|
||||
"furn.cat_kitchen_sink": "Kitchen sinks",
|
||||
"furn.cat_nightstand": "Cabinets",
|
||||
"furn.cat_plant": "Plants",
|
||||
"furn.cat_rug": "Rugs",
|
||||
"furn.cat_shelving": "Shelving",
|
||||
"furn.cat_shower": "Showers",
|
||||
"furn.cat_sink": "Sinks",
|
||||
"furn.cat_sofa": "Sofas",
|
||||
"furn.cat_stairs": "Stairs",
|
||||
"furn.cat_toilet": "Toilets",
|
||||
"furn.cat_tv": "Televisions",
|
||||
"furn.cat_wardrobe": "Wardrobes",
|
||||
"furn.cat_washer": "Washing machines",
|
||||
"furn.cat_work_table": "Desks",
|
||||
"furn.sym_sofa": "Two-seat sofa",
|
||||
"furn.sym_sofa_three_seat": "Three-seat sofa",
|
||||
"furn.sym_sofa_corner_right": "Right sectional sofa",
|
||||
"furn.sym_armchair": "Soft armchair",
|
||||
"furn.sym_armchair_office": "Office chair",
|
||||
"furn.sym_coffee_table": "Rectangular coffee table",
|
||||
"furn.sym_coffee_table_round": "Round coffee table",
|
||||
"furn.sym_coffee_table_oval": "Oval coffee table",
|
||||
"furn.sym_coffee_table_rounded": "Rounded coffee table",
|
||||
"furn.sym_table_dining": "Rectangular dining table",
|
||||
"furn.sym_table_round": "Round table",
|
||||
"furn.sym_table_dining_oval": "Oval dining table",
|
||||
"furn.sym_table_dining_rounded": "Rounded dining table",
|
||||
"furn.sym_chair": "Chair",
|
||||
"furn.sym_desk": "Desk",
|
||||
"furn.sym_chair_bar": "Bar stool",
|
||||
"furn.sym_desk": "Rectangular desk",
|
||||
"furn.sym_desk_corner": "Corner desk",
|
||||
"furn.sym_bed_double": "Double bed",
|
||||
"furn.sym_bed_single": "Single bed",
|
||||
"furn.sym_nightstand": "Nightstand",
|
||||
"furn.sym_nightstand": "Bedside table",
|
||||
"furn.sym_cabinet_tv": "TV cabinet",
|
||||
"furn.sym_cabinet_shoe": "Shoe cabinet",
|
||||
"furn.sym_cabinet_sink": "Sink cabinet",
|
||||
"furn.sym_wardrobe": "Wardrobe",
|
||||
"furn.sym_bookshelf": "Bookshelf",
|
||||
"furn.sym_bookshelf": "Bookcase",
|
||||
"furn.sym_wall_unit": "Wall unit",
|
||||
"furn.sym_kitchen_floor": "Kitchen floor module",
|
||||
"furn.sym_kitchen_floor_corner": "Kitchen floor corner module",
|
||||
"furn.sym_kitchen_wall": "Kitchen wall module",
|
||||
"furn.sym_kitchen_wall_corner": "Kitchen wall corner module",
|
||||
"furn.sym_shelf_floor": "Floor shelving unit",
|
||||
"furn.sym_shelf_wall": "Wall shelf",
|
||||
"furn.sym_fridge": "Fridge",
|
||||
"furn.sym_stove": "Cooker",
|
||||
"furn.sym_stove": "Four-burner cooktop",
|
||||
"furn.sym_cooktop_two": "Two-burner cooktop",
|
||||
"furn.sym_dishwasher": "Dishwasher",
|
||||
"furn.sym_washer": "Washing machine",
|
||||
"furn.sym_dryer": "Tumble dryer",
|
||||
"furn.sym_tv": "TV",
|
||||
"furn.sym_tv": "TV on stand",
|
||||
"furn.sym_tv_wall": "Wall-mounted TV",
|
||||
"furn.sym_ac": "Air conditioner",
|
||||
"furn.sym_water_heater": "Water heater",
|
||||
"furn.sym_toilet": "Toilet",
|
||||
"furn.sym_bathtub": "Bathtub",
|
||||
"furn.sym_toilet": "Floor-standing toilet",
|
||||
"furn.sym_toilet_built_in": "Built-in toilet",
|
||||
"furn.sym_bathtub": "Rectangular bathtub",
|
||||
"furn.sym_bathtub_corner": "Corner bathtub",
|
||||
"furn.sym_shower": "Shower",
|
||||
"furn.sym_sink": "Washbasin",
|
||||
"furn.sym_kitchen_sink": "Kitchen sink",
|
||||
"furn.sym_bidet": "Bidet",
|
||||
"furn.sym_kitchen_sink": "Single kitchen sink",
|
||||
"furn.sym_kitchen_sink_double": "Double kitchen sink",
|
||||
"furn.sym_bidet": "Floor-standing bidet",
|
||||
"furn.sym_bidet_built_in": "Built-in bidet",
|
||||
"furn.sym_stairs": "Stairs",
|
||||
"furn.sym_fireplace": "Fireplace",
|
||||
"furn.sym_plant": "Plant",
|
||||
|
||||
+71
-15
@@ -900,34 +900,90 @@
|
||||
"furn.group_other": "Прочее",
|
||||
"furn.width": "Ширина",
|
||||
"furn.depth": "Глубина",
|
||||
"furn.pick_hint": "Выберите символ и кликните по плану.",
|
||||
"furn.back_to_categories": "Все категории",
|
||||
"furn.pick_hint": "Выберите категорию, затем символ.",
|
||||
"furn.place_hint": "Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.",
|
||||
"furn.sym_sofa": "Диван",
|
||||
"furn.sym_armchair": "Кресло",
|
||||
"furn.sym_coffee_table": "Журнальный столик",
|
||||
"furn.sym_table_dining": "Обеденный стол",
|
||||
"furn.cat_air_conditioner": "Кондиционеры",
|
||||
"furn.cat_armchair": "Кресла",
|
||||
"furn.cat_bathtub": "Ванны",
|
||||
"furn.cat_bed": "Кровати",
|
||||
"furn.cat_bidet": "Биде",
|
||||
"furn.cat_boiler": "Бойлеры",
|
||||
"furn.cat_chair": "Стулья",
|
||||
"furn.cat_coffee_table": "Журнальные столы",
|
||||
"furn.cat_cooktop": "Варочные панели",
|
||||
"furn.cat_dining_table": "Обеденные столы",
|
||||
"furn.cat_dishwasher": "Посудомоечные машины",
|
||||
"furn.cat_dryer": "Сушильные машины",
|
||||
"furn.cat_fireplace": "Камины",
|
||||
"furn.cat_fridge": "Холодильники",
|
||||
"furn.cat_kitchen_cabinet": "Кухонные шкафы",
|
||||
"furn.cat_kitchen_sink": "Кухонные мойки",
|
||||
"furn.cat_nightstand": "Тумбы",
|
||||
"furn.cat_plant": "Растения",
|
||||
"furn.cat_rug": "Ковры",
|
||||
"furn.cat_shelving": "Стеллажи",
|
||||
"furn.cat_shower": "Душевые",
|
||||
"furn.cat_sink": "Раковины",
|
||||
"furn.cat_sofa": "Диваны",
|
||||
"furn.cat_stairs": "Лестницы",
|
||||
"furn.cat_toilet": "Унитазы",
|
||||
"furn.cat_tv": "Телевизоры",
|
||||
"furn.cat_wardrobe": "Шкафы",
|
||||
"furn.cat_washer": "Стиральные машины",
|
||||
"furn.cat_work_table": "Рабочие столы",
|
||||
"furn.sym_sofa": "Диван двухместный",
|
||||
"furn.sym_sofa_three_seat": "Диван трёхместный",
|
||||
"furn.sym_sofa_corner_right": "Угловой диван, правый",
|
||||
"furn.sym_armchair": "Кресло мягкое",
|
||||
"furn.sym_armchair_office": "Кресло офисное",
|
||||
"furn.sym_coffee_table": "Журнальный стол, прямоугольный",
|
||||
"furn.sym_coffee_table_round": "Журнальный стол, круглый",
|
||||
"furn.sym_coffee_table_oval": "Журнальный стол, овальный",
|
||||
"furn.sym_coffee_table_rounded": "Журнальный стол, скруглённый",
|
||||
"furn.sym_table_dining": "Обеденный стол, прямоугольный",
|
||||
"furn.sym_table_round": "Круглый стол",
|
||||
"furn.sym_table_dining_oval": "Обеденный стол, овальный",
|
||||
"furn.sym_table_dining_rounded": "Обеденный стол, скруглённый",
|
||||
"furn.sym_chair": "Стул",
|
||||
"furn.sym_desk": "Письменный стол",
|
||||
"furn.sym_chair_bar": "Барный стул",
|
||||
"furn.sym_desk": "Рабочий стол, прямоугольный",
|
||||
"furn.sym_desk_corner": "Рабочий стол, угловой",
|
||||
"furn.sym_bed_double": "Двуспальная кровать",
|
||||
"furn.sym_bed_single": "Односпальная кровать",
|
||||
"furn.sym_nightstand": "Тумбочка",
|
||||
"furn.sym_wardrobe": "Шкаф",
|
||||
"furn.sym_bookshelf": "Стеллаж",
|
||||
"furn.sym_nightstand": "Тумба прикроватная",
|
||||
"furn.sym_cabinet_tv": "Тумба под телевизор",
|
||||
"furn.sym_cabinet_shoe": "Тумба для обуви",
|
||||
"furn.sym_cabinet_sink": "Тумба под раковину",
|
||||
"furn.sym_wardrobe": "Шкаф гардеробный",
|
||||
"furn.sym_bookshelf": "Шкаф книжный",
|
||||
"furn.sym_wall_unit": "Шкаф-стенка",
|
||||
"furn.sym_kitchen_floor": "Кухонный напольный модуль",
|
||||
"furn.sym_kitchen_floor_corner": "Кухонный напольный угловой модуль",
|
||||
"furn.sym_kitchen_wall": "Кухонный навесной модуль",
|
||||
"furn.sym_kitchen_wall_corner": "Кухонный навесной угловой модуль",
|
||||
"furn.sym_shelf_floor": "Стеллаж напольный",
|
||||
"furn.sym_shelf_wall": "Полка настенная",
|
||||
"furn.sym_fridge": "Холодильник",
|
||||
"furn.sym_stove": "Плита",
|
||||
"furn.sym_stove": "Варочная панель, 4 конфорки",
|
||||
"furn.sym_cooktop_two": "Варочная панель, 2 конфорки",
|
||||
"furn.sym_dishwasher": "Посудомоечная машина",
|
||||
"furn.sym_washer": "Стиральная машина",
|
||||
"furn.sym_dryer": "Сушильная машина",
|
||||
"furn.sym_tv": "Телевизор",
|
||||
"furn.sym_tv": "Телевизор на подставке",
|
||||
"furn.sym_tv_wall": "Телевизор на кронштейне",
|
||||
"furn.sym_ac": "Кондиционер",
|
||||
"furn.sym_water_heater": "Бойлер",
|
||||
"furn.sym_toilet": "Унитаз",
|
||||
"furn.sym_bathtub": "Ванна",
|
||||
"furn.sym_toilet": "Унитаз напольный",
|
||||
"furn.sym_toilet_built_in": "Унитаз встроенный",
|
||||
"furn.sym_bathtub": "Ванна прямоугольная",
|
||||
"furn.sym_bathtub_corner": "Ванна угловая",
|
||||
"furn.sym_shower": "Душ",
|
||||
"furn.sym_sink": "Раковина",
|
||||
"furn.sym_kitchen_sink": "Кухонная мойка",
|
||||
"furn.sym_bidet": "Биде",
|
||||
"furn.sym_kitchen_sink": "Кухонная мойка одинарная",
|
||||
"furn.sym_kitchen_sink_double": "Кухонная мойка двойная",
|
||||
"furn.sym_bidet": "Биде напольное",
|
||||
"furn.sym_bidet_built_in": "Биде встроенное",
|
||||
"furn.sym_stairs": "Лестница",
|
||||
"furn.sym_fireplace": "Камин",
|
||||
"furn.sym_plant": "Растение",
|
||||
|
||||
@@ -695,6 +695,11 @@ export const dialogsStyles = css`
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
.furnback {
|
||||
min-height: 34px;
|
||||
gap: 5px;
|
||||
margin: 2px 0 4px;
|
||||
}
|
||||
.furnitem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -722,6 +727,9 @@ export const dialogsStyles = css`
|
||||
height: 40px;
|
||||
color: var(--primary-text-color, currentColor);
|
||||
}
|
||||
.furncategory { width: 92px; min-height: 76px; }
|
||||
.furncatprev { width: 48px; height: 48px; }
|
||||
.furnvariants .furnitem { min-height: 74px; }
|
||||
.furnsize {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user