@astuteo/breakout-grid 5.1.1 → 5.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  (function() {
2
2
  "use strict";
3
- const VERSION = `v${"5.1.1"} lite`;
3
+ const VERSION = `v${"5.1.3"} lite`;
4
4
  const LOREM_CONTENT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
5
5
 
6
6
  Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet.`;
@@ -1,6 +1,6 @@
1
1
  (function() {
2
2
  "use strict";
3
- const VERSION = `v${"5.1.1"}`;
3
+ const VERSION = `v${"5.1.3"}`;
4
4
  const LOREM_CONTENT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
5
5
 
6
6
  Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet.`;
@@ -12,29 +12,26 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
12
12
  { name: "content", label: "Content", className: ".col-content", color: "rgba(168, 85, 247, 0.25)", borderColor: "rgb(168, 85, 247)" }
13
13
  ];
14
14
  const CONFIG_OPTIONS = {
15
- // Base measurements
16
15
  baseGap: { value: "1rem", desc: "Minimum gap between columns. Use rem.", cssVar: "--config-base-gap", liveVar: "--base-gap" },
17
16
  maxGap: { value: "15rem", desc: "Maximum gap cap for ultra-wide. Use rem.", cssVar: "--config-max-gap", liveVar: "--max-gap" },
18
- contentMin: { value: "53rem", desc: "Min width for content column (~848px). Use rem.", cssVar: "--config-content-min", liveVar: "--content-min" },
19
- contentMax: { value: "61rem", desc: "Max width for content column (~976px). Use rem.", cssVar: "--config-content-max", liveVar: "--content-max" },
17
+ contentMin: { value: "50rem", desc: "Min width for content column (~848px). Use rem.", cssVar: "--config-content-min", liveVar: "--content-min" },
18
+ contentMax: { value: "55rem", desc: "Max width for content column (~976px). Use rem.", cssVar: "--config-content-max", liveVar: "--content-max" },
20
19
  contentBase: { value: "75vw", desc: "Preferred width for content (fluid). Use vw.", cssVar: "--config-content-base", liveVar: "--content-base" },
21
- // Track widths
22
- popoutWidth: { value: "5rem", desc: "Popout extends beyond content. Use rem.", cssVar: "--config-popout", liveVar: null },
23
- featureMin: { value: "0rem", desc: "Minimum feature track width (floor)", cssVar: "--config-feature-min", liveVar: null },
24
- featureScale: { value: "12vw", desc: "Fluid feature track scaling", cssVar: "--config-feature-scale", liveVar: null },
25
- featureMax: { value: "12rem", desc: "Maximum feature track width (ceiling)", cssVar: "--config-feature-max", liveVar: null },
20
+ popoutWidth: { value: "5rem", desc: "Popout extends beyond content. Use rem.", cssVar: "--config-popout", liveVar: "--popout-width" },
21
+ featureMin: { value: "0rem", desc: "Minimum feature track width (floor)", cssVar: "--config-feature-min", liveVar: "--feature-min" },
22
+ featureScale: { value: "12vw", desc: "Fluid feature track scaling", cssVar: "--config-feature-scale", liveVar: "--feature-scale" },
23
+ featureMax: { value: "12rem", desc: "Maximum feature track width (ceiling)", cssVar: "--config-feature-max", liveVar: "--feature-max" },
26
24
  fullLimit: { value: "115rem", desc: "Max width for col-full-limit. Use rem.", cssVar: "--config-full-limit", liveVar: "--full-limit" },
27
- // Default column
28
- defaultCol: { value: "content", desc: "Default column when no col-* class", type: "select", options: ["content", "popout", "feature", "full"], cssVar: "--config-default-col" }
25
+ defaultCol: { value: "content", desc: "Default column when no col-* class", type: "select", options: ["content", "popout", "feature", "full"], cssVar: "--config-default-col", liveVar: "--default-col" }
29
26
  };
30
27
  const GAP_SCALE_OPTIONS = {
31
- default: { value: "4vw", desc: "Mobile/default gap scaling. Use vw.", cssVar: "--config-gap-scale-default" },
32
- lg: { value: "5vw", desc: "Large screens (1024px+). Use vw.", cssVar: "--config-gap-scale-lg" },
33
- xl: { value: "6vw", desc: "Extra large (1280px+). Use vw.", cssVar: "--config-gap-scale-xl" }
28
+ default: { value: "4vw", desc: "Mobile/default gap scaling. Use vw.", cssVar: "--config-gap-scale-default", liveVar: "--gap-scale-default" },
29
+ lg: { value: "5vw", desc: "Large screens (1024px+). Use vw.", cssVar: "--config-gap-scale-lg", liveVar: "--gap-scale-lg" },
30
+ xl: { value: "6vw", desc: "Extra large (1280px+). Use vw.", cssVar: "--config-gap-scale-xl", liveVar: "--gap-scale-xl" }
34
31
  };
35
32
  const BREAKOUT_OPTIONS = {
36
- min: { value: "1rem", desc: "Minimum breakout padding (floor)", cssVar: "--config-breakout-min" },
37
- scale: { value: "5vw", desc: "Fluid breakout scaling", cssVar: "--config-breakout-scale" }
33
+ min: { value: "1rem", desc: "Minimum breakout padding (floor)", cssVar: "--config-breakout-min", liveVar: "--breakout-min" },
34
+ scale: { value: "5vw", desc: "Fluid breakout scaling", cssVar: "--config-breakout-scale", liveVar: "--breakout-scale" }
38
35
  // max is popoutWidth
39
36
  };
40
37
  const BREAKPOINT_OPTIONS = {
@@ -43,7 +40,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
43
40
  };
44
41
  function createInitialState() {
45
42
  return {
46
- // UI State
47
43
  isVisible: false,
48
44
  showLabels: true,
49
45
  showClassNames: true,
@@ -65,38 +61,28 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
65
61
  editorPos: { x: 20, y: 100 },
66
62
  isDragging: false,
67
63
  dragOffset: { x: 0, y: 0 },
68
- // Column resize drag state
69
64
  resizingColumn: null,
70
65
  resizeStartX: 0,
71
66
  resizeStartValue: 0,
72
- // Panel collapse state
73
67
  controlPanelCollapsed: false,
74
68
  configEditorCollapsed: false,
75
- // Computed column widths in pixels (pre-initialized for reactivity)
76
- columnWidths: {
77
- full: 0,
78
- "full-limit": 0,
79
- feature: 0,
80
- popout: 0,
81
- content: 0,
82
- center: 0
83
- },
84
- // Current breakpoint for gap scale (mobile, lg, xl)
69
+ // Pre-initialized for Alpine reactivity
70
+ columnWidths: { full: 0, "full-limit": 0, feature: 0, popout: 0, content: 0, center: 0 },
85
71
  currentBreakpoint: "mobile",
86
- // Spacing panel state
87
72
  spacingPanelCollapsed: false,
88
73
  spacingPanelPos: { x: 16, y: 16 },
89
74
  isDraggingSpacing: false,
90
75
  dragOffsetSpacing: { x: 0, y: 0 },
91
- // Restore config modal
92
76
  showRestoreModal: false,
93
77
  restoreInput: "",
94
78
  restoreError: null,
95
- // Section copy feedback
96
- sectionCopied: null
79
+ sectionCopied: null,
80
+ showCloseWarningModal: false,
81
+ gridOpacity: 0.8,
82
+ backdropOpacity: 0.85
97
83
  };
98
84
  }
99
- const BUILD_VERSION = "5.1.1";
85
+ const BUILD_VERSION = "5.1.3";
100
86
  function generateCSSExport(c, version = BUILD_VERSION) {
101
87
  var _a, _b, _c, _d, _e;
102
88
  const VERSION2 = version;
@@ -763,7 +749,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
763
749
  `;
764
750
  }
765
751
  const methods = {
766
- // Initialize
767
752
  init() {
768
753
  const saved = localStorage.getItem("breakoutGridVisualizerVisible");
769
754
  if (saved !== null) {
@@ -818,12 +803,10 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
818
803
  this.updateCurrentBreakpoint();
819
804
  console.log("Breakout Grid Visualizer loaded. Press Ctrl/Cmd + G to toggle.");
820
805
  },
821
- // Toggle visibility
822
806
  toggle() {
823
807
  this.isVisible = !this.isVisible;
824
808
  localStorage.setItem("breakoutGridVisualizerVisible", this.isVisible);
825
809
  },
826
- // Update column widths by querying DOM elements
827
810
  updateColumnWidths() {
828
811
  this.$nextTick(() => {
829
812
  this.gridAreas.forEach((area) => {
@@ -834,7 +817,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
834
817
  });
835
818
  });
836
819
  },
837
- // Detect current breakpoint based on viewport width
838
820
  updateCurrentBreakpoint() {
839
821
  const width = window.innerWidth;
840
822
  if (width >= 1280) {
@@ -845,7 +827,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
845
827
  this.currentBreakpoint = "mobile";
846
828
  }
847
829
  },
848
- // Update --gap live based on current breakpoint and edit values
849
830
  updateGapLive() {
850
831
  const scaleKey = this.currentBreakpoint === "mobile" ? "default" : this.currentBreakpoint;
851
832
  const base = this.editValues.baseGap || this.configOptions.baseGap.value;
@@ -854,7 +835,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
854
835
  document.documentElement.style.setProperty("--gap", `clamp(${base}, ${scale}, ${max})`);
855
836
  this.updateColumnWidths();
856
837
  },
857
- // Check if content width exceeds comfortable reading width (55rem)
858
838
  getContentReadabilityWarning() {
859
839
  const contentMax = parseFloat(this.editValues.contentMax || this.configOptions.contentMax.value);
860
840
  if (contentMax > 55) {
@@ -862,7 +842,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
862
842
  }
863
843
  return null;
864
844
  },
865
- // Check if configured track widths would exceed viewport
866
845
  getTrackOverflowWarning() {
867
846
  const contentMax = parseFloat(this.editValues.contentMax || this.configOptions.contentMax.value) * 16;
868
847
  const featureMax = parseFloat(this.editValues.featureMax || this.configOptions.featureMax.value) * 16;
@@ -875,31 +854,29 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
875
854
  }
876
855
  return null;
877
856
  },
878
- // Get computed CSS variable value
879
857
  getCSSVariable(varName) {
880
858
  const value = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
881
859
  return value || "Not set";
882
860
  },
883
- // Helper to load options from CSS variables
884
861
  loadOptionsFromCSS(options, prefix = "") {
885
862
  Object.keys(options).forEach((key) => {
886
863
  const opt = options[key];
887
864
  const editKey = prefix ? `${prefix}_${key}` : key;
888
- if (opt.cssVar) {
889
- const computed = this.getCSSVariable(opt.cssVar);
890
- this.editValues[editKey] = computed && computed !== "Not set" && computed !== "" ? computed : opt.value;
891
- } else {
892
- this.editValues[editKey] = opt.value;
865
+ let computed = null;
866
+ if (opt.liveVar) {
867
+ computed = this.getCSSVariable(opt.liveVar);
868
+ }
869
+ if ((!computed || computed === "Not set" || computed === "") && opt.cssVar) {
870
+ computed = this.getCSSVariable(opt.cssVar);
893
871
  }
872
+ this.editValues[editKey] = computed && computed !== "Not set" && computed !== "" ? computed : opt.value;
894
873
  });
895
874
  },
896
- // Load current values from CSS variables where available
897
875
  loadCurrentValues() {
898
876
  this.loadOptionsFromCSS(this.configOptions);
899
877
  this.loadOptionsFromCSS(this.gapScaleOptions, "gapScale");
900
878
  this.loadOptionsFromCSS(this.breakoutOptions, "breakout");
901
879
  },
902
- // Generate export config object
903
880
  generateConfigExport() {
904
881
  var _a, _b, _c, _d;
905
882
  const config = {};
@@ -918,7 +895,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
918
895
  };
919
896
  return config;
920
897
  },
921
- // Format config object with single quotes for values, no quotes for keys
922
898
  formatConfig(obj, indent = 2) {
923
899
  const pad = " ".repeat(indent);
924
900
  const lines = ["{"];
@@ -934,35 +910,14 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
934
910
  lines.push("}");
935
911
  return lines.join("\n");
936
912
  },
937
- // Section definitions for partial copying
938
913
  configSections: {
939
- content: {
940
- keys: ["contentMin", "contentBase", "contentMax"],
941
- label: "Content"
942
- },
943
- defaultCol: {
944
- keys: ["defaultCol"],
945
- label: "Default Column"
946
- },
947
- tracks: {
948
- keys: ["popoutWidth", "fullLimit"],
949
- label: "Track Widths"
950
- },
951
- feature: {
952
- keys: ["featureMin", "featureScale", "featureMax"],
953
- label: "Feature"
954
- },
955
- gap: {
956
- keys: ["baseGap", "maxGap"],
957
- nested: { gapScale: ["default", "lg", "xl"] },
958
- label: "Gap"
959
- },
960
- breakout: {
961
- keys: ["breakoutMin", "breakoutScale"],
962
- label: "Breakout"
963
- }
914
+ content: { keys: ["contentMin", "contentBase", "contentMax"], label: "Content" },
915
+ defaultCol: { keys: ["defaultCol"], label: "Default Column" },
916
+ tracks: { keys: ["popoutWidth", "fullLimit"], label: "Track Widths" },
917
+ feature: { keys: ["featureMin", "featureScale", "featureMax"], label: "Feature" },
918
+ gap: { keys: ["baseGap", "maxGap"], nested: { gapScale: ["default", "lg", "xl"] }, label: "Gap" },
919
+ breakout: { keys: ["breakoutMin", "breakoutScale"], label: "Breakout" }
964
920
  },
965
- // Copy a specific section to clipboard
966
921
  copySection(sectionName) {
967
922
  const section = this.configSections[sectionName];
968
923
  if (!section) return;
@@ -990,7 +945,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
990
945
  setTimeout(() => this.sectionCopied = null, 1500);
991
946
  });
992
947
  },
993
- // Format config as flat key-value pairs (no wrapping braces)
994
948
  formatConfigFlat(obj) {
995
949
  const lines = [];
996
950
  const entries = Object.entries(obj);
@@ -1004,7 +958,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1004
958
  });
1005
959
  return lines.join("\n");
1006
960
  },
1007
- // Copy config to clipboard as CSS variables
1008
961
  copyConfig() {
1009
962
  var _a, _b, _c, _d, _e;
1010
963
  const config = this.generateConfigExport();
@@ -1045,7 +998,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1045
998
  setTimeout(() => this.copySuccess = false, 2e3);
1046
999
  });
1047
1000
  },
1048
- // Generate and download standalone CSS file
1049
1001
  downloadCSS() {
1050
1002
  const css = this.generateCSSExport(this.generateConfigExport());
1051
1003
  const blob = new Blob([css], { type: "text/css" });
@@ -1056,7 +1008,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1056
1008
  a.click();
1057
1009
  URL.revokeObjectURL(url);
1058
1010
  },
1059
- // Parse CSS value into number and unit (e.g., "4rem" -> { num: 4, unit: "rem" })
1060
1011
  parseValue(val) {
1061
1012
  const match = String(val).match(/^([\d.]+)(.*)$/);
1062
1013
  if (match) {
@@ -1064,29 +1015,23 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1064
1015
  }
1065
1016
  return { num: 0, unit: "rem" };
1066
1017
  },
1067
- // Get the numeric part of a config value
1068
1018
  getNumericValue(key) {
1069
1019
  const val = this.editValues[key] || this.configOptions[key].value;
1070
1020
  return this.parseValue(val).num;
1071
1021
  },
1072
- // Get the unit part of a config value
1073
1022
  getUnit(key) {
1074
1023
  const val = this.editValues[key] || this.configOptions[key].value;
1075
1024
  return this.parseValue(val).unit;
1076
1025
  },
1077
- // Check if a field should have unit selection (rem-based fields only)
1078
1026
  hasUnitSelector(key) {
1079
1027
  const unit = this.getUnit(key);
1080
1028
  return unit === "rem" || unit === "ch" || unit === "px";
1081
1029
  },
1082
- // Available units for selection
1083
1030
  unitOptions: ["rem", "ch", "px"],
1084
- // Update just the unit, keeping the numeric value
1085
1031
  updateUnit(key, newUnit) {
1086
1032
  const num = this.getNumericValue(key);
1087
1033
  this.updateConfigValue(key, num + newUnit);
1088
1034
  },
1089
- // Update just the numeric part, keeping the unit
1090
1035
  updateNumericValue(key, num) {
1091
1036
  if (key === "content" && num < 1) num = 1;
1092
1037
  if (key === "baseGap" && num < 0) num = 0;
@@ -1095,7 +1040,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1095
1040
  const unit = this.getUnit(key);
1096
1041
  this.updateConfigValue(key, num + unit);
1097
1042
  },
1098
- // Generic getter for prefixed options (gapScale, breakout)
1099
1043
  getPrefixedNumeric(prefix, options, key) {
1100
1044
  const val = this.editValues[`${prefix}_${key}`] || options[key].value;
1101
1045
  return this.parseValue(val).num;
@@ -1104,7 +1048,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1104
1048
  const val = this.editValues[`${prefix}_${key}`] || options[key].value;
1105
1049
  return this.parseValue(val).unit;
1106
1050
  },
1107
- // Gap scale helpers (use generic)
1108
1051
  getGapScaleNumeric(key) {
1109
1052
  return this.getPrefixedNumeric("gapScale", this.gapScaleOptions, key);
1110
1053
  },
@@ -1117,7 +1060,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1117
1060
  this.updateGapLive();
1118
1061
  this.saveConfigToStorage();
1119
1062
  },
1120
- // Breakout helpers (use generic)
1121
1063
  getBreakoutNumeric(key) {
1122
1064
  return this.getPrefixedNumeric("breakout", this.breakoutOptions, key);
1123
1065
  },
@@ -1130,19 +1072,16 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1130
1072
  this.updateBreakoutLive();
1131
1073
  this.saveConfigToStorage();
1132
1074
  },
1133
- // Update --breakout-padding live
1134
1075
  updateBreakoutLive() {
1135
1076
  const min = this.editValues.breakout_min || this.breakoutOptions.min.value;
1136
1077
  const scale = this.editValues.breakout_scale || this.breakoutOptions.scale.value;
1137
1078
  const max = this.editValues.popoutWidth || this.configOptions.popoutWidth.value;
1138
1079
  document.documentElement.style.setProperty("--breakout-padding", `clamp(${min}, ${scale}, ${max})`);
1139
1080
  },
1140
- // Save current config to localStorage
1141
1081
  saveConfigToStorage() {
1142
1082
  const config = this.generateConfigExport();
1143
1083
  localStorage.setItem("breakoutGridConfig", JSON.stringify(config));
1144
1084
  },
1145
- // Apply a config object (used by restore and localStorage load)
1146
1085
  applyConfig(config) {
1147
1086
  this.editMode = true;
1148
1087
  Object.keys(this.configOptions).forEach((key) => {
@@ -1188,7 +1127,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1188
1127
  }
1189
1128
  this.updateColumnWidths();
1190
1129
  },
1191
- // Reset config to defaults and clear localStorage
1192
1130
  resetConfigToDefaults() {
1193
1131
  if (!confirm("Reset all config values to defaults?")) return;
1194
1132
  localStorage.removeItem("breakoutGridConfig");
@@ -1196,7 +1134,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1196
1134
  this.loadCurrentValues();
1197
1135
  this.configCopied = false;
1198
1136
  },
1199
- // Update a config value (and live CSS var if applicable)
1200
1137
  updateConfigValue(key, value) {
1201
1138
  this.editValues[key] = value;
1202
1139
  this.configCopied = false;
@@ -1222,15 +1159,12 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1222
1159
  this.updateGapLive();
1223
1160
  }
1224
1161
  },
1225
- // Select a grid area
1226
1162
  selectArea(areaName) {
1227
1163
  this.selectedArea = this.selectedArea === areaName ? null : areaName;
1228
1164
  },
1229
- // Check if area is selected
1230
1165
  isSelected(areaName) {
1231
1166
  return this.selectedArea === areaName;
1232
1167
  },
1233
- // Restore all CSS variable overrides to original values
1234
1168
  restoreCSSVariables() {
1235
1169
  Object.keys(this.configOptions).forEach((key) => {
1236
1170
  const opt = this.configOptions[key];
@@ -1246,7 +1180,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1246
1180
  this.editValues = {};
1247
1181
  this.configCopied = false;
1248
1182
  },
1249
- // Toggle edit mode
1250
1183
  toggleEditMode() {
1251
1184
  this.editMode = !this.editMode;
1252
1185
  if (this.editMode) {
@@ -1255,30 +1188,37 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1255
1188
  this.restoreCSSVariables();
1256
1189
  }
1257
1190
  },
1258
- // Check if any values have been edited and not yet copied
1259
1191
  hasUnsavedEdits() {
1260
1192
  return Object.keys(this.editValues).length > 0 && !this.configCopied;
1261
1193
  },
1262
- // Open floating editor
1263
1194
  openEditor() {
1264
1195
  this.showEditor = true;
1265
1196
  this.editMode = true;
1266
1197
  this.loadCurrentValues();
1267
1198
  localStorage.setItem("breakoutGridEditorOpen", "true");
1268
1199
  },
1269
- // Close floating editor
1270
1200
  closeEditor(force = false) {
1271
1201
  if (!force && this.hasUnsavedEdits()) {
1272
- if (!confirm("You have unsaved config changes. Close without copying?")) {
1273
- return;
1274
- }
1202
+ this.showCloseWarningModal = true;
1203
+ return;
1275
1204
  }
1276
1205
  this.showEditor = false;
1277
1206
  this.editMode = false;
1278
1207
  this.restoreCSSVariables();
1279
1208
  localStorage.setItem("breakoutGridEditorOpen", "false");
1280
1209
  },
1281
- // Generic drag handling for panels
1210
+ closeWarningCopyAndClose() {
1211
+ this.copyConfig();
1212
+ this.showCloseWarningModal = false;
1213
+ this.closeEditor(true);
1214
+ },
1215
+ closeWarningDiscard() {
1216
+ this.showCloseWarningModal = false;
1217
+ this.closeEditor(true);
1218
+ },
1219
+ closeWarningGoBack() {
1220
+ this.showCloseWarningModal = false;
1221
+ },
1282
1222
  _dragConfigs: {
1283
1223
  editor: { pos: "editorPos", dragging: "isDragging", offset: "dragOffset", storage: "breakoutGridEditorPos" },
1284
1224
  spacing: { pos: "spacingPanelPos", dragging: "isDraggingSpacing", offset: "dragOffsetSpacing", storage: "breakoutGridSpacingPos" }
@@ -1299,7 +1239,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1299
1239
  if (this[cfg.dragging]) localStorage.setItem(cfg.storage, JSON.stringify(this[cfg.pos]));
1300
1240
  this[cfg.dragging] = false;
1301
1241
  },
1302
- // Editor drag (shorthand)
1303
1242
  startDrag(e) {
1304
1243
  this.startPanelDrag(e, "editor");
1305
1244
  },
@@ -1309,7 +1248,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1309
1248
  stopDrag() {
1310
1249
  this.stopPanelDrag("editor");
1311
1250
  },
1312
- // Spacing drag (shorthand)
1313
1251
  startDragSpacing(e) {
1314
1252
  this.startPanelDrag(e, "spacing");
1315
1253
  },
@@ -1319,7 +1257,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1319
1257
  stopDragSpacing() {
1320
1258
  this.stopPanelDrag("spacing");
1321
1259
  },
1322
- // Column resize drag handling
1323
1260
  startColumnResize(e, columnType) {
1324
1261
  if (!this.editMode) return;
1325
1262
  e.preventDefault();
@@ -1353,18 +1290,14 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1353
1290
  stopColumnResize() {
1354
1291
  this.resizingColumn = null;
1355
1292
  },
1356
- // Map column names to their config keys for resizing
1357
1293
  getResizeConfig(colName) {
1358
1294
  const map = {
1359
1295
  "full-limit": "fullLimit",
1360
1296
  "feature": "featureScale",
1361
1297
  "popout": "popoutWidth"
1362
- // content has its own integrated handles for min/max/base
1363
- // feature has its own integrated handles for min/scale/max
1364
1298
  };
1365
1299
  return map[colName] || null;
1366
1300
  },
1367
- // Parse a CSS variables string into a config object
1368
1301
  parseConfigString(input) {
1369
1302
  const str = input.trim();
1370
1303
  const config = { gapScale: {}, breakpoints: {} };
@@ -1412,19 +1345,16 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1412
1345
  }
1413
1346
  return config;
1414
1347
  },
1415
- // Open restore modal
1416
1348
  openRestoreModal() {
1417
1349
  this.showRestoreModal = true;
1418
1350
  this.restoreInput = "";
1419
1351
  this.restoreError = null;
1420
1352
  },
1421
- // Close restore modal
1422
1353
  closeRestoreModal() {
1423
1354
  this.showRestoreModal = false;
1424
1355
  this.restoreInput = "";
1425
1356
  this.restoreError = null;
1426
1357
  },
1427
- // Apply a parsed config to the editor
1428
1358
  restoreConfig() {
1429
1359
  this.restoreError = null;
1430
1360
  try {
@@ -1482,7 +1412,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1482
1412
  x-transition:leave="transition ease-in duration-150"
1483
1413
  x-transition:leave-start="opacity-100"
1484
1414
  x-transition:leave-end="opacity-0"
1485
- style="position: absolute; inset: 0; background: rgba(255, 255, 255, 0.85); z-index: 1;"></div>
1415
+ :style="{ position: 'absolute', inset: 0, background: 'rgba(255, 255, 255, ' + backdropOpacity + ')', zIndex: 1 }"></div>
1486
1416
 
1487
1417
  <!-- Advanced Span Examples Overlay -->
1488
1418
  <div x-show="showAdvanced"
@@ -1752,7 +1682,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1752
1682
  </div>
1753
1683
 
1754
1684
  <!-- Grid Overlay (hidden in Advanced mode) -->
1755
- <div x-show="!showAdvanced" x-init="$watch('isVisible', v => v && setTimeout(() => updateColumnWidths(), 50)); setTimeout(() => updateColumnWidths(), 100)" class="grid-cols-breakout breakout-visualizer-grid" style="height: 100%; position: relative; z-index: 2;">
1685
+ <div x-show="!showAdvanced" x-init="$watch('isVisible', v => v && setTimeout(() => updateColumnWidths(), 50)); setTimeout(() => updateColumnWidths(), 100)" class="grid-cols-breakout breakout-visualizer-grid" :style="{ height: '100%', position: 'relative', zIndex: 2, opacity: gridOpacity }">
1756
1686
  <template x-for="area in gridAreas" :key="area.name">
1757
1687
  <div :class="'col-' + area.name"
1758
1688
  @click="selectArea(area.name)"
@@ -2157,6 +2087,22 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2157
2087
  </div>
2158
2088
  </div>
2159
2089
 
2090
+ <!-- Opacity Sliders -->
2091
+ <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2092
+ <div style="display: flex; flex-direction: column; gap: 6px;">
2093
+ <div style="display: flex; align-items: center; gap: 12px;">
2094
+ <span style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; width: 28px; flex-shrink: 0;">Grid</span>
2095
+ <input type="range" x-model="gridOpacity" min="0.1" max="1" step="0.1"
2096
+ style="flex: 1; height: 4px; cursor: pointer; accent-color: #1a1a2e;">
2097
+ </div>
2098
+ <div style="display: flex; align-items: center; gap: 12px;">
2099
+ <span style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; width: 28px; flex-shrink: 0;">Page</span>
2100
+ <input type="range" x-model="backdropOpacity" min="0" max="1" step="0.05"
2101
+ style="flex: 1; height: 4px; cursor: pointer; accent-color: #1a1a2e;">
2102
+ </div>
2103
+ </div>
2104
+ </div>
2105
+
2160
2106
  <!-- Padding Options -->
2161
2107
  <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2162
2108
  <div style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px;">Padding</div>
@@ -2237,6 +2183,23 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2237
2183
  <span style="font-weight: 600;">⚠️</span> <span x-text="getTrackOverflowWarning()"></span>
2238
2184
  </div>
2239
2185
 
2186
+ <!-- Default Column Section -->
2187
+ <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2188
+ <div style="display: flex; align-items: center; justify-content: space-between;">
2189
+ <div @click="copySection('defaultCol')" style="cursor: pointer;">
2190
+ <div style="font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;" :style="{ color: sectionCopied === 'defaultCol' ? '#10b981' : '#6b7280' }" x-text="sectionCopied === 'defaultCol' ? '✓ Copied' : 'Default Column'"></div>
2191
+ <div style="font-size: 9px; color: #9ca3af; margin-top: 2px;">For children without col-* class</div>
2192
+ </div>
2193
+ <select @change="editValues.defaultCol = $event.target.value; configCopied = false"
2194
+ :value="editValues.defaultCol || configOptions.defaultCol.value"
2195
+ style="padding: 6px 8px; font-size: 11px; border: 1px solid #e5e5e5; border-radius: 4px; background: #f9fafb; cursor: pointer;">
2196
+ <template x-for="opt in configOptions.defaultCol.options" :key="opt">
2197
+ <option :value="opt" :selected="(editValues.defaultCol || configOptions.defaultCol.value) === opt" x-text="opt"></option>
2198
+ </template>
2199
+ </select>
2200
+ </div>
2201
+ </div>
2202
+
2240
2203
  <!-- Content Section -->
2241
2204
  <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2242
2205
  <div @click="copySection('content')" style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; cursor: pointer; display: flex; align-items: center; gap: 6px;" :style="{ color: sectionCopied === 'content' ? '#10b981' : '#6b7280' }">
@@ -2282,23 +2245,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2282
2245
  </div>
2283
2246
  </div>
2284
2247
 
2285
- <!-- Default Column Section -->
2286
- <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2287
- <div style="display: flex; align-items: center; justify-content: space-between;">
2288
- <div @click="copySection('defaultCol')" style="cursor: pointer;">
2289
- <div style="font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;" :style="{ color: sectionCopied === 'defaultCol' ? '#10b981' : '#6b7280' }" x-text="sectionCopied === 'defaultCol' ? '✓ Copied' : 'Default Column'"></div>
2290
- <div style="font-size: 9px; color: #9ca3af; margin-top: 2px;">For children without col-* class</div>
2291
- </div>
2292
- <select @change="editValues.defaultCol = $event.target.value; configCopied = false"
2293
- :value="editValues.defaultCol || configOptions.defaultCol.value"
2294
- style="padding: 6px 8px; font-size: 11px; border: 1px solid #e5e5e5; border-radius: 4px; background: #f9fafb; cursor: pointer;">
2295
- <template x-for="opt in configOptions.defaultCol.options" :key="opt">
2296
- <option :value="opt" :selected="(editValues.defaultCol || configOptions.defaultCol.value) === opt" x-text="opt"></option>
2297
- </template>
2298
- </select>
2299
- </div>
2300
- </div>
2301
-
2302
2248
  <!-- Track Widths Section -->
2303
2249
  <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2304
2250
  <div @click="copySection('tracks')" style="font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; cursor: pointer;" :style="{ color: sectionCopied === 'tracks' ? '#10b981' : '#6b7280' }" x-text="sectionCopied === 'tracks' ? '✓ Copied' : 'Track Widths'"></div>
@@ -2490,6 +2436,53 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2490
2436
  </div>
2491
2437
  </div>
2492
2438
 
2439
+ <!-- Close Warning Modal -->
2440
+ <div x-show="showCloseWarningModal"
2441
+ x-transition:enter="transition ease-out duration-200"
2442
+ x-transition:enter-start="opacity-0"
2443
+ x-transition:enter-end="opacity-100"
2444
+ x-transition:leave="transition ease-in duration-150"
2445
+ x-transition:leave-start="opacity-100"
2446
+ x-transition:leave-end="opacity-0"
2447
+ style="position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 10003; pointer-events: auto;">
2448
+ <div @click.stop style="background: white; border-radius: 8px; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25); width: 420px; max-width: 90vw; font-family: system-ui, -apple-system, sans-serif;">
2449
+ <!-- Modal Header -->
2450
+ <div style="padding: 12px 16px; background: #fef3c7; color: #92400e; border-radius: 8px 8px 0 0; display: flex; align-items: center; gap: 10px;">
2451
+ <svg style="width: 20px; height: 20px; flex-shrink: 0;" fill="currentColor" viewBox="0 0 20 20">
2452
+ <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
2453
+ </svg>
2454
+ <span style="font-weight: 600; font-size: 14px;">Unsaved Configuration</span>
2455
+ </div>
2456
+ <!-- Modal Body -->
2457
+ <div style="padding: 16px;">
2458
+ <p style="font-size: 13px; color: #374151; margin: 0 0 12px 0; line-height: 1.5;">
2459
+ Your grid configuration changes are stored in <strong>browser localStorage</strong> and will only apply while the visualizer is loaded.
2460
+ </p>
2461
+ <div style="background: #f3f4f6; border-radius: 6px; padding: 12px; margin-bottom: 12px;">
2462
+ <p style="font-size: 12px; color: #4b5563; margin: 0 0 8px 0; line-height: 1.5;">
2463
+ <strong>To make changes permanent:</strong>
2464
+ </p>
2465
+ <ol style="font-size: 11px; color: #6b7280; margin: 0; padding-left: 16px; line-height: 1.6;">
2466
+ <li>Copy the config to your clipboard</li>
2467
+ <li>Paste into your project's CSS file</li>
2468
+ <li>Remove the visualizer script from production</li>
2469
+ </ol>
2470
+ </div>
2471
+ <p style="font-size: 11px; color: #9ca3af; margin: 0; line-height: 1.4;">
2472
+ Without copying, your changes will be lost when the visualizer is removed.
2473
+ </p>
2474
+ </div>
2475
+ <!-- Modal Footer -->
2476
+ <div style="padding: 12px 16px; background: #f7f7f7; border-radius: 0 0 8px 8px; display: flex; justify-content: space-between; gap: 8px;">
2477
+ <button @click="closeWarningDiscard()" style="padding: 8px 16px; font-size: 11px; font-weight: 600; border: 1px solid #fca5a5; border-radius: 4px; cursor: pointer; background: #fef2f2; color: #dc2626;">Discard Changes</button>
2478
+ <div style="display: flex; gap: 8px;">
2479
+ <button @click="closeWarningGoBack()" style="padding: 8px 16px; font-size: 11px; font-weight: 600; border: 1px solid #e5e5e5; border-radius: 4px; cursor: pointer; background: white; color: #374151;">Go Back</button>
2480
+ <button @click="closeWarningCopyAndClose()" style="padding: 8px 16px; font-size: 11px; font-weight: 600; border: none; border-radius: 4px; cursor: pointer; background: #1a1a2e; color: white;">Copy & Close</button>
2481
+ </div>
2482
+ </div>
2483
+ </div>
2484
+ </div>
2485
+
2493
2486
  <!-- Grid Diagram -->
2494
2487
  <div x-show="showDiagram"
2495
2488
  style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 0.5rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); pointer-events: auto; z-index: 10001; padding: 1.5rem; max-width: 90vw;">
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Breakout Grid - Objects Layer (ITCSS)
3
- * Version: 5.1.1
3
+ * Version: 5.1.3
4
4
  *
5
5
  * Documentation: https://github.com/astuteo-llc/breakout-grid
6
6
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astuteo/breakout-grid",
3
- "version": "5.1.1",
3
+ "version": "5.1.3",
4
4
  "type": "module",
5
5
  "description": "CSS Grid breakout layout system with visual configurator",
6
6
  "main": "dist/_objects.breakout-grid.css",