Configurable grid + snap-to-grid in the layout system (#45)
Build image / build-and-push (push) Successful in 7s
Build image / build-and-push (push) Successful in 7s
Closes #44. Two independent grids (garden + per-bed), each with a size and a snap toggle; snapping defaults off. See PR #45 for details. Co-authored-by: Steve Dudenhoeffer <[email protected]>
This commit was merged in pull request #45.
This commit is contained in:
+35
-16
@@ -13,11 +13,12 @@ import (
|
||||
// sanity range that also rejects NaN/Inf and absurd values. Name/notes are
|
||||
// length-capped so untrusted input can't balloon storage.
|
||||
const (
|
||||
defaultGardenCM = 1000 // 10 m
|
||||
minGardenCM = 1 // 1 cm
|
||||
maxGardenCM = 10_000 // 100 m
|
||||
maxGardenNameLen = 200
|
||||
maxGardenNotesLen = 10_000
|
||||
defaultGardenCM = 1000 // 10 m
|
||||
minGardenCM = 1 // 1 cm
|
||||
maxGardenCM = 10_000 // 100 m
|
||||
defaultGardenGridCM = 100 // 1 m, matching the editor's previous fixed grid
|
||||
maxGardenNameLen = 200
|
||||
maxGardenNotesLen = 10_000
|
||||
)
|
||||
|
||||
// gardenRole ranks a user's access to a garden. Higher includes lower
|
||||
@@ -48,11 +49,13 @@ func (r gardenRole) String() string {
|
||||
|
||||
// GardenInput is the mutable field set for creating or updating a garden.
|
||||
type GardenInput struct {
|
||||
Name string
|
||||
WidthCM float64
|
||||
HeightCM float64
|
||||
UnitPref string
|
||||
Notes string
|
||||
Name string
|
||||
WidthCM float64
|
||||
HeightCM float64
|
||||
UnitPref string
|
||||
Notes string
|
||||
GridSizeCM float64
|
||||
SnapToGrid bool
|
||||
}
|
||||
|
||||
// requireGardenRole loads a garden and enforces that the actor holds at least
|
||||
@@ -163,7 +166,9 @@ func (s *Service) DeleteGarden(ctx context.Context, actorID, gardenID int64) err
|
||||
|
||||
// gardenFromInput validates and normalizes input into a domain.Garden (without
|
||||
// identity/ownership fields). With applyDefaults, absent dimensions/unit are
|
||||
// filled in (create); without it, every field must be supplied (update).
|
||||
// filled in (create); without it, dimensions and unit must be supplied (update).
|
||||
// The grid size is the exception: 0 always means "unset" and is defaulted on both
|
||||
// paths, so an older client that omits it can't wipe the column to an invalid 0.
|
||||
func gardenFromInput(in GardenInput, applyDefaults bool) (*domain.Garden, error) {
|
||||
name := strings.TrimSpace(in.Name)
|
||||
if name == "" || len(name) > maxGardenNameLen {
|
||||
@@ -198,12 +203,26 @@ func gardenFromInput(in GardenInput, applyDefaults bool) (*domain.Garden, error)
|
||||
return nil, domain.ErrInvalidInput
|
||||
}
|
||||
|
||||
// Grid spacing shares the garden dimension range [1 cm, 100 m]. 0 means
|
||||
// "unset" — defaulted (not just on create: an older client that omits it must
|
||||
// not wipe the column to an invalid 0), so a saved garden always has a usable
|
||||
// grid even if snapping is off.
|
||||
grid := in.GridSizeCM
|
||||
if grid == 0 {
|
||||
grid = defaultGardenGridCM
|
||||
}
|
||||
if !validDimensionCM(grid) {
|
||||
return nil, domain.ErrInvalidInput
|
||||
}
|
||||
|
||||
return &domain.Garden{
|
||||
Name: name,
|
||||
WidthCM: width,
|
||||
HeightCM: height,
|
||||
UnitPref: unit,
|
||||
Notes: notes,
|
||||
Name: name,
|
||||
WidthCM: width,
|
||||
HeightCM: height,
|
||||
UnitPref: unit,
|
||||
Notes: notes,
|
||||
GridSizeCM: grid,
|
||||
SnapToGrid: in.SnapToGrid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user