@astuteo/breakout-grid 5.1.0 → 5.1.2

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.0"} lite`;
3
+ const VERSION = `v${"5.1.2"} 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.0"}`;
3
+ const VERSION = `v${"5.1.2"}`;
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,19 +12,16 @@ 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
20
  popoutWidth: { value: "5rem", desc: "Popout extends beyond content. Use rem.", cssVar: "--config-popout", liveVar: null },
23
21
  featureMin: { value: "0rem", desc: "Minimum feature track width (floor)", cssVar: "--config-feature-min", liveVar: null },
24
22
  featureScale: { value: "12vw", desc: "Fluid feature track scaling", cssVar: "--config-feature-scale", liveVar: null },
25
23
  featureMax: { value: "12rem", desc: "Maximum feature track width (ceiling)", cssVar: "--config-feature-max", liveVar: null },
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
25
  defaultCol: { value: "content", desc: "Default column when no col-* class", type: "select", options: ["content", "popout", "feature", "full"], cssVar: "--config-default-col" }
29
26
  };
30
27
  const GAP_SCALE_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.0";
85
+ const BUILD_VERSION = "5.1.2";
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) {
@@ -775,6 +760,14 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
775
760
  this.editMode = true;
776
761
  this.$nextTick(() => this.loadCurrentValues());
777
762
  }
763
+ const savedConfig = localStorage.getItem("breakoutGridConfig");
764
+ if (savedConfig) {
765
+ try {
766
+ const config = JSON.parse(savedConfig);
767
+ this.$nextTick(() => this.applyConfig(config));
768
+ } catch (e) {
769
+ }
770
+ }
778
771
  const editorPos = localStorage.getItem("breakoutGridEditorPos");
779
772
  if (editorPos) {
780
773
  try {
@@ -810,12 +803,10 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
810
803
  this.updateCurrentBreakpoint();
811
804
  console.log("Breakout Grid Visualizer loaded. Press Ctrl/Cmd + G to toggle.");
812
805
  },
813
- // Toggle visibility
814
806
  toggle() {
815
807
  this.isVisible = !this.isVisible;
816
808
  localStorage.setItem("breakoutGridVisualizerVisible", this.isVisible);
817
809
  },
818
- // Update column widths by querying DOM elements
819
810
  updateColumnWidths() {
820
811
  this.$nextTick(() => {
821
812
  this.gridAreas.forEach((area) => {
@@ -826,7 +817,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
826
817
  });
827
818
  });
828
819
  },
829
- // Detect current breakpoint based on viewport width
830
820
  updateCurrentBreakpoint() {
831
821
  const width = window.innerWidth;
832
822
  if (width >= 1280) {
@@ -837,7 +827,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
837
827
  this.currentBreakpoint = "mobile";
838
828
  }
839
829
  },
840
- // Update --gap live based on current breakpoint and edit values
841
830
  updateGapLive() {
842
831
  const scaleKey = this.currentBreakpoint === "mobile" ? "default" : this.currentBreakpoint;
843
832
  const base = this.editValues.baseGap || this.configOptions.baseGap.value;
@@ -846,7 +835,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
846
835
  document.documentElement.style.setProperty("--gap", `clamp(${base}, ${scale}, ${max})`);
847
836
  this.updateColumnWidths();
848
837
  },
849
- // Check if content width exceeds comfortable reading width (55rem)
850
838
  getContentReadabilityWarning() {
851
839
  const contentMax = parseFloat(this.editValues.contentMax || this.configOptions.contentMax.value);
852
840
  if (contentMax > 55) {
@@ -854,7 +842,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
854
842
  }
855
843
  return null;
856
844
  },
857
- // Check if configured track widths would exceed viewport
858
845
  getTrackOverflowWarning() {
859
846
  const contentMax = parseFloat(this.editValues.contentMax || this.configOptions.contentMax.value) * 16;
860
847
  const featureMax = parseFloat(this.editValues.featureMax || this.configOptions.featureMax.value) * 16;
@@ -867,12 +854,10 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
867
854
  }
868
855
  return null;
869
856
  },
870
- // Get computed CSS variable value
871
857
  getCSSVariable(varName) {
872
858
  const value = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
873
859
  return value || "Not set";
874
860
  },
875
- // Helper to load options from CSS variables
876
861
  loadOptionsFromCSS(options, prefix = "") {
877
862
  Object.keys(options).forEach((key) => {
878
863
  const opt = options[key];
@@ -885,13 +870,11 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
885
870
  }
886
871
  });
887
872
  },
888
- // Load current values from CSS variables where available
889
873
  loadCurrentValues() {
890
874
  this.loadOptionsFromCSS(this.configOptions);
891
875
  this.loadOptionsFromCSS(this.gapScaleOptions, "gapScale");
892
876
  this.loadOptionsFromCSS(this.breakoutOptions, "breakout");
893
877
  },
894
- // Generate export config object
895
878
  generateConfigExport() {
896
879
  var _a, _b, _c, _d;
897
880
  const config = {};
@@ -910,7 +893,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
910
893
  };
911
894
  return config;
912
895
  },
913
- // Format config object with single quotes for values, no quotes for keys
914
896
  formatConfig(obj, indent = 2) {
915
897
  const pad = " ".repeat(indent);
916
898
  const lines = ["{"];
@@ -926,35 +908,14 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
926
908
  lines.push("}");
927
909
  return lines.join("\n");
928
910
  },
929
- // Section definitions for partial copying
930
911
  configSections: {
931
- content: {
932
- keys: ["contentMin", "contentBase", "contentMax"],
933
- label: "Content"
934
- },
935
- defaultCol: {
936
- keys: ["defaultCol"],
937
- label: "Default Column"
938
- },
939
- tracks: {
940
- keys: ["popoutWidth", "fullLimit"],
941
- label: "Track Widths"
942
- },
943
- feature: {
944
- keys: ["featureMin", "featureScale", "featureMax"],
945
- label: "Feature"
946
- },
947
- gap: {
948
- keys: ["baseGap", "maxGap"],
949
- nested: { gapScale: ["default", "lg", "xl"] },
950
- label: "Gap"
951
- },
952
- breakout: {
953
- keys: ["breakoutMin", "breakoutScale"],
954
- label: "Breakout"
955
- }
912
+ content: { keys: ["contentMin", "contentBase", "contentMax"], label: "Content" },
913
+ defaultCol: { keys: ["defaultCol"], label: "Default Column" },
914
+ tracks: { keys: ["popoutWidth", "fullLimit"], label: "Track Widths" },
915
+ feature: { keys: ["featureMin", "featureScale", "featureMax"], label: "Feature" },
916
+ gap: { keys: ["baseGap", "maxGap"], nested: { gapScale: ["default", "lg", "xl"] }, label: "Gap" },
917
+ breakout: { keys: ["breakoutMin", "breakoutScale"], label: "Breakout" }
956
918
  },
957
- // Copy a specific section to clipboard
958
919
  copySection(sectionName) {
959
920
  const section = this.configSections[sectionName];
960
921
  if (!section) return;
@@ -982,7 +943,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
982
943
  setTimeout(() => this.sectionCopied = null, 1500);
983
944
  });
984
945
  },
985
- // Format config as flat key-value pairs (no wrapping braces)
986
946
  formatConfigFlat(obj) {
987
947
  const lines = [];
988
948
  const entries = Object.entries(obj);
@@ -996,7 +956,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
996
956
  });
997
957
  return lines.join("\n");
998
958
  },
999
- // Copy config to clipboard as CSS variables
1000
959
  copyConfig() {
1001
960
  var _a, _b, _c, _d, _e;
1002
961
  const config = this.generateConfigExport();
@@ -1037,7 +996,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1037
996
  setTimeout(() => this.copySuccess = false, 2e3);
1038
997
  });
1039
998
  },
1040
- // Generate and download standalone CSS file
1041
999
  downloadCSS() {
1042
1000
  const css = this.generateCSSExport(this.generateConfigExport());
1043
1001
  const blob = new Blob([css], { type: "text/css" });
@@ -1048,7 +1006,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1048
1006
  a.click();
1049
1007
  URL.revokeObjectURL(url);
1050
1008
  },
1051
- // Parse CSS value into number and unit (e.g., "4rem" -> { num: 4, unit: "rem" })
1052
1009
  parseValue(val) {
1053
1010
  const match = String(val).match(/^([\d.]+)(.*)$/);
1054
1011
  if (match) {
@@ -1056,29 +1013,23 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1056
1013
  }
1057
1014
  return { num: 0, unit: "rem" };
1058
1015
  },
1059
- // Get the numeric part of a config value
1060
1016
  getNumericValue(key) {
1061
1017
  const val = this.editValues[key] || this.configOptions[key].value;
1062
1018
  return this.parseValue(val).num;
1063
1019
  },
1064
- // Get the unit part of a config value
1065
1020
  getUnit(key) {
1066
1021
  const val = this.editValues[key] || this.configOptions[key].value;
1067
1022
  return this.parseValue(val).unit;
1068
1023
  },
1069
- // Check if a field should have unit selection (rem-based fields only)
1070
1024
  hasUnitSelector(key) {
1071
1025
  const unit = this.getUnit(key);
1072
1026
  return unit === "rem" || unit === "ch" || unit === "px";
1073
1027
  },
1074
- // Available units for selection
1075
1028
  unitOptions: ["rem", "ch", "px"],
1076
- // Update just the unit, keeping the numeric value
1077
1029
  updateUnit(key, newUnit) {
1078
1030
  const num = this.getNumericValue(key);
1079
1031
  this.updateConfigValue(key, num + newUnit);
1080
1032
  },
1081
- // Update just the numeric part, keeping the unit
1082
1033
  updateNumericValue(key, num) {
1083
1034
  if (key === "content" && num < 1) num = 1;
1084
1035
  if (key === "baseGap" && num < 0) num = 0;
@@ -1087,7 +1038,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1087
1038
  const unit = this.getUnit(key);
1088
1039
  this.updateConfigValue(key, num + unit);
1089
1040
  },
1090
- // Generic getter for prefixed options (gapScale, breakout)
1091
1041
  getPrefixedNumeric(prefix, options, key) {
1092
1042
  const val = this.editValues[`${prefix}_${key}`] || options[key].value;
1093
1043
  return this.parseValue(val).num;
@@ -1096,7 +1046,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1096
1046
  const val = this.editValues[`${prefix}_${key}`] || options[key].value;
1097
1047
  return this.parseValue(val).unit;
1098
1048
  },
1099
- // Gap scale helpers (use generic)
1100
1049
  getGapScaleNumeric(key) {
1101
1050
  return this.getPrefixedNumeric("gapScale", this.gapScaleOptions, key);
1102
1051
  },
@@ -1107,8 +1056,8 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1107
1056
  this.editValues[`gapScale_${key}`] = num + this.getGapScaleUnit(key);
1108
1057
  this.configCopied = false;
1109
1058
  this.updateGapLive();
1059
+ this.saveConfigToStorage();
1110
1060
  },
1111
- // Breakout helpers (use generic)
1112
1061
  getBreakoutNumeric(key) {
1113
1062
  return this.getPrefixedNumeric("breakout", this.breakoutOptions, key);
1114
1063
  },
@@ -1119,18 +1068,74 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1119
1068
  this.editValues[`breakout_${key}`] = num + this.getBreakoutUnit(key);
1120
1069
  this.configCopied = false;
1121
1070
  this.updateBreakoutLive();
1071
+ this.saveConfigToStorage();
1122
1072
  },
1123
- // Update --breakout-padding live
1124
1073
  updateBreakoutLive() {
1125
1074
  const min = this.editValues.breakout_min || this.breakoutOptions.min.value;
1126
1075
  const scale = this.editValues.breakout_scale || this.breakoutOptions.scale.value;
1127
1076
  const max = this.editValues.popoutWidth || this.configOptions.popoutWidth.value;
1128
1077
  document.documentElement.style.setProperty("--breakout-padding", `clamp(${min}, ${scale}, ${max})`);
1129
1078
  },
1130
- // Update a config value (and live CSS var if applicable)
1079
+ saveConfigToStorage() {
1080
+ const config = this.generateConfigExport();
1081
+ localStorage.setItem("breakoutGridConfig", JSON.stringify(config));
1082
+ },
1083
+ applyConfig(config) {
1084
+ this.editMode = true;
1085
+ Object.keys(this.configOptions).forEach((key) => {
1086
+ if (config[key] !== void 0) {
1087
+ this.editValues[key] = config[key];
1088
+ const opt = this.configOptions[key];
1089
+ if (opt && opt.liveVar) {
1090
+ document.documentElement.style.setProperty(opt.liveVar, config[key]);
1091
+ }
1092
+ }
1093
+ });
1094
+ if (config.popoutWidth) {
1095
+ document.documentElement.style.setProperty("--popout", `minmax(0, ${config.popoutWidth})`);
1096
+ }
1097
+ if (config.featureMin || config.featureScale || config.featureMax) {
1098
+ const featureMin = config.featureMin || this.configOptions.featureMin.value;
1099
+ const featureScale = config.featureScale || this.configOptions.featureScale.value;
1100
+ const featureMax = config.featureMax || this.configOptions.featureMax.value;
1101
+ document.documentElement.style.setProperty("--feature", `minmax(0, clamp(${featureMin}, ${featureScale}, ${featureMax}))`);
1102
+ }
1103
+ if (config.gapScale) {
1104
+ Object.keys(this.gapScaleOptions).forEach((key) => {
1105
+ if (config.gapScale[key] !== void 0) {
1106
+ this.editValues[`gapScale_${key}`] = config.gapScale[key];
1107
+ }
1108
+ });
1109
+ this.updateGapLive();
1110
+ }
1111
+ if (config.breakoutMin !== void 0) {
1112
+ this.editValues.breakout_min = config.breakoutMin;
1113
+ }
1114
+ if (config.breakoutScale !== void 0) {
1115
+ this.editValues.breakout_scale = config.breakoutScale;
1116
+ }
1117
+ this.updateBreakoutLive();
1118
+ if (config.breakpoints) {
1119
+ if (config.breakpoints.lg !== void 0) {
1120
+ this.editValues.breakpoint_lg = config.breakpoints.lg;
1121
+ }
1122
+ if (config.breakpoints.xl !== void 0) {
1123
+ this.editValues.breakpoint_xl = config.breakpoints.xl;
1124
+ }
1125
+ }
1126
+ this.updateColumnWidths();
1127
+ },
1128
+ resetConfigToDefaults() {
1129
+ if (!confirm("Reset all config values to defaults?")) return;
1130
+ localStorage.removeItem("breakoutGridConfig");
1131
+ this.restoreCSSVariables();
1132
+ this.loadCurrentValues();
1133
+ this.configCopied = false;
1134
+ },
1131
1135
  updateConfigValue(key, value) {
1132
1136
  this.editValues[key] = value;
1133
1137
  this.configCopied = false;
1138
+ this.saveConfigToStorage();
1134
1139
  const opt = this.configOptions[key];
1135
1140
  if (opt && opt.liveVar) {
1136
1141
  document.documentElement.style.setProperty(opt.liveVar, value);
@@ -1152,15 +1157,12 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1152
1157
  this.updateGapLive();
1153
1158
  }
1154
1159
  },
1155
- // Select a grid area
1156
1160
  selectArea(areaName) {
1157
1161
  this.selectedArea = this.selectedArea === areaName ? null : areaName;
1158
1162
  },
1159
- // Check if area is selected
1160
1163
  isSelected(areaName) {
1161
1164
  return this.selectedArea === areaName;
1162
1165
  },
1163
- // Restore all CSS variable overrides to original values
1164
1166
  restoreCSSVariables() {
1165
1167
  Object.keys(this.configOptions).forEach((key) => {
1166
1168
  const opt = this.configOptions[key];
@@ -1176,7 +1178,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1176
1178
  this.editValues = {};
1177
1179
  this.configCopied = false;
1178
1180
  },
1179
- // Toggle edit mode
1180
1181
  toggleEditMode() {
1181
1182
  this.editMode = !this.editMode;
1182
1183
  if (this.editMode) {
@@ -1185,30 +1186,37 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1185
1186
  this.restoreCSSVariables();
1186
1187
  }
1187
1188
  },
1188
- // Check if any values have been edited and not yet copied
1189
1189
  hasUnsavedEdits() {
1190
1190
  return Object.keys(this.editValues).length > 0 && !this.configCopied;
1191
1191
  },
1192
- // Open floating editor
1193
1192
  openEditor() {
1194
1193
  this.showEditor = true;
1195
1194
  this.editMode = true;
1196
1195
  this.loadCurrentValues();
1197
1196
  localStorage.setItem("breakoutGridEditorOpen", "true");
1198
1197
  },
1199
- // Close floating editor
1200
1198
  closeEditor(force = false) {
1201
1199
  if (!force && this.hasUnsavedEdits()) {
1202
- if (!confirm("You have unsaved config changes. Close without copying?")) {
1203
- return;
1204
- }
1200
+ this.showCloseWarningModal = true;
1201
+ return;
1205
1202
  }
1206
1203
  this.showEditor = false;
1207
1204
  this.editMode = false;
1208
1205
  this.restoreCSSVariables();
1209
1206
  localStorage.setItem("breakoutGridEditorOpen", "false");
1210
1207
  },
1211
- // Generic drag handling for panels
1208
+ closeWarningCopyAndClose() {
1209
+ this.copyConfig();
1210
+ this.showCloseWarningModal = false;
1211
+ this.closeEditor(true);
1212
+ },
1213
+ closeWarningDiscard() {
1214
+ this.showCloseWarningModal = false;
1215
+ this.closeEditor(true);
1216
+ },
1217
+ closeWarningGoBack() {
1218
+ this.showCloseWarningModal = false;
1219
+ },
1212
1220
  _dragConfigs: {
1213
1221
  editor: { pos: "editorPos", dragging: "isDragging", offset: "dragOffset", storage: "breakoutGridEditorPos" },
1214
1222
  spacing: { pos: "spacingPanelPos", dragging: "isDraggingSpacing", offset: "dragOffsetSpacing", storage: "breakoutGridSpacingPos" }
@@ -1229,7 +1237,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1229
1237
  if (this[cfg.dragging]) localStorage.setItem(cfg.storage, JSON.stringify(this[cfg.pos]));
1230
1238
  this[cfg.dragging] = false;
1231
1239
  },
1232
- // Editor drag (shorthand)
1233
1240
  startDrag(e) {
1234
1241
  this.startPanelDrag(e, "editor");
1235
1242
  },
@@ -1239,7 +1246,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1239
1246
  stopDrag() {
1240
1247
  this.stopPanelDrag("editor");
1241
1248
  },
1242
- // Spacing drag (shorthand)
1243
1249
  startDragSpacing(e) {
1244
1250
  this.startPanelDrag(e, "spacing");
1245
1251
  },
@@ -1249,7 +1255,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1249
1255
  stopDragSpacing() {
1250
1256
  this.stopPanelDrag("spacing");
1251
1257
  },
1252
- // Column resize drag handling
1253
1258
  startColumnResize(e, columnType) {
1254
1259
  if (!this.editMode) return;
1255
1260
  e.preventDefault();
@@ -1283,18 +1288,14 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1283
1288
  stopColumnResize() {
1284
1289
  this.resizingColumn = null;
1285
1290
  },
1286
- // Map column names to their config keys for resizing
1287
1291
  getResizeConfig(colName) {
1288
1292
  const map = {
1289
1293
  "full-limit": "fullLimit",
1290
1294
  "feature": "featureScale",
1291
1295
  "popout": "popoutWidth"
1292
- // content has its own integrated handles for min/max/base
1293
- // feature has its own integrated handles for min/scale/max
1294
1296
  };
1295
1297
  return map[colName] || null;
1296
1298
  },
1297
- // Parse a CSS variables string into a config object
1298
1299
  parseConfigString(input) {
1299
1300
  const str = input.trim();
1300
1301
  const config = { gapScale: {}, breakpoints: {} };
@@ -1342,19 +1343,16 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1342
1343
  }
1343
1344
  return config;
1344
1345
  },
1345
- // Open restore modal
1346
1346
  openRestoreModal() {
1347
1347
  this.showRestoreModal = true;
1348
1348
  this.restoreInput = "";
1349
1349
  this.restoreError = null;
1350
1350
  },
1351
- // Close restore modal
1352
1351
  closeRestoreModal() {
1353
1352
  this.showRestoreModal = false;
1354
1353
  this.restoreInput = "";
1355
1354
  this.restoreError = null;
1356
1355
  },
1357
- // Apply a parsed config to the editor
1358
1356
  restoreConfig() {
1359
1357
  this.restoreError = null;
1360
1358
  try {
@@ -1412,7 +1410,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1412
1410
  x-transition:leave="transition ease-in duration-150"
1413
1411
  x-transition:leave-start="opacity-100"
1414
1412
  x-transition:leave-end="opacity-0"
1415
- style="position: absolute; inset: 0; background: rgba(255, 255, 255, 0.85); z-index: 1;"></div>
1413
+ :style="{ position: 'absolute', inset: 0, background: 'rgba(255, 255, 255, ' + backdropOpacity + ')', zIndex: 1 }"></div>
1416
1414
 
1417
1415
  <!-- Advanced Span Examples Overlay -->
1418
1416
  <div x-show="showAdvanced"
@@ -1682,7 +1680,7 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
1682
1680
  </div>
1683
1681
 
1684
1682
  <!-- Grid Overlay (hidden in Advanced mode) -->
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; z-index: 2;">
1683
+ <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 }">
1686
1684
  <template x-for="area in gridAreas" :key="area.name">
1687
1685
  <div :class="'col-' + area.name"
1688
1686
  @click="selectArea(area.name)"
@@ -2087,6 +2085,22 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2087
2085
  </div>
2088
2086
  </div>
2089
2087
 
2088
+ <!-- Opacity Sliders -->
2089
+ <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2090
+ <div style="display: flex; flex-direction: column; gap: 6px;">
2091
+ <div style="display: flex; align-items: center; gap: 12px;">
2092
+ <span style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; width: 28px; flex-shrink: 0;">Grid</span>
2093
+ <input type="range" x-model="gridOpacity" min="0.1" max="1" step="0.1"
2094
+ style="flex: 1; height: 4px; cursor: pointer; accent-color: #1a1a2e;">
2095
+ </div>
2096
+ <div style="display: flex; align-items: center; gap: 12px;">
2097
+ <span style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; width: 28px; flex-shrink: 0;">Page</span>
2098
+ <input type="range" x-model="backdropOpacity" min="0" max="1" step="0.05"
2099
+ style="flex: 1; height: 4px; cursor: pointer; accent-color: #1a1a2e;">
2100
+ </div>
2101
+ </div>
2102
+ </div>
2103
+
2090
2104
  <!-- Padding Options -->
2091
2105
  <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2092
2106
  <div style="font-size: 9px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px;">Padding</div>
@@ -2167,6 +2181,23 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2167
2181
  <span style="font-weight: 600;">⚠️</span> <span x-text="getTrackOverflowWarning()"></span>
2168
2182
  </div>
2169
2183
 
2184
+ <!-- Default Column Section -->
2185
+ <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2186
+ <div style="display: flex; align-items: center; justify-content: space-between;">
2187
+ <div @click="copySection('defaultCol')" style="cursor: pointer;">
2188
+ <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>
2189
+ <div style="font-size: 9px; color: #9ca3af; margin-top: 2px;">For children without col-* class</div>
2190
+ </div>
2191
+ <select @change="editValues.defaultCol = $event.target.value; configCopied = false"
2192
+ :value="editValues.defaultCol || configOptions.defaultCol.value"
2193
+ style="padding: 6px 8px; font-size: 11px; border: 1px solid #e5e5e5; border-radius: 4px; background: #f9fafb; cursor: pointer;">
2194
+ <template x-for="opt in configOptions.defaultCol.options" :key="opt">
2195
+ <option :value="opt" :selected="(editValues.defaultCol || configOptions.defaultCol.value) === opt" x-text="opt"></option>
2196
+ </template>
2197
+ </select>
2198
+ </div>
2199
+ </div>
2200
+
2170
2201
  <!-- Content Section -->
2171
2202
  <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2172
2203
  <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' }">
@@ -2212,23 +2243,6 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2212
2243
  </div>
2213
2244
  </div>
2214
2245
 
2215
- <!-- Default Column Section -->
2216
- <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2217
- <div style="display: flex; align-items: center; justify-content: space-between;">
2218
- <div @click="copySection('defaultCol')" style="cursor: pointer;">
2219
- <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>
2220
- <div style="font-size: 9px; color: #9ca3af; margin-top: 2px;">For children without col-* class</div>
2221
- </div>
2222
- <select @change="editValues.defaultCol = $event.target.value; configCopied = false"
2223
- :value="editValues.defaultCol || configOptions.defaultCol.value"
2224
- style="padding: 6px 8px; font-size: 11px; border: 1px solid #e5e5e5; border-radius: 4px; background: #f9fafb; cursor: pointer;">
2225
- <template x-for="opt in configOptions.defaultCol.options" :key="opt">
2226
- <option :value="opt" :selected="(editValues.defaultCol || configOptions.defaultCol.value) === opt" x-text="opt"></option>
2227
- </template>
2228
- </select>
2229
- </div>
2230
- </div>
2231
-
2232
2246
  <!-- Track Widths Section -->
2233
2247
  <div style="padding: 8px 12px; background: white; border-bottom: 1px solid #e5e5e5;">
2234
2248
  <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>
@@ -2360,15 +2374,20 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2360
2374
  </div>
2361
2375
 
2362
2376
  <!-- Action Buttons -->
2363
- <div style="padding: 10px 12px; background: #f7f7f7; display: flex; gap: 8px;">
2364
- <button @click="copyConfig()" :style="{ flex: 1, padding: '8px', fontSize: '11px', fontWeight: '600', border: 'none', borderRadius: '4px', cursor: 'pointer', background: copySuccess ? '#10b981' : '#1a1a2e', color: 'white', transition: 'background 0.2s' }">
2365
- <span x-text="copySuccess ? ' Copied' : 'Copy Variables'"></span>
2366
- </button>
2367
- <button @click="openRestoreModal()" style="padding: 8px 12px; font-size: 11px; font-weight: 600; border: 1px solid #e5e5e5; border-radius: 4px; cursor: pointer; background: white; color: #374151;" title="Restore from CSS variables">
2368
- Restore
2369
- </button>
2370
- <button @click="downloadCSS()" style="padding: 8px 12px; font-size: 11px; font-weight: 600; border: 1px solid #e5e5e5; border-radius: 4px; cursor: pointer; background: white; color: #374151;">
2371
- CSS
2377
+ <div style="padding: 10px 12px; background: #f7f7f7; display: flex; flex-direction: column; gap: 8px;">
2378
+ <div style="display: flex; gap: 8px;">
2379
+ <button @click="copyConfig()" :style="{ flex: 1, padding: '8px', fontSize: '11px', fontWeight: '500', border: '1px solid #e5e5e5', borderRadius: '4px', cursor: 'pointer', background: copySuccess ? '#10b981' : 'white', color: copySuccess ? 'white' : '#374151', transition: 'background 0.2s' }">
2380
+ <span x-text="copySuccess ? '✓ Copied' : 'Copy'"></span>
2381
+ </button>
2382
+ <button @click="openRestoreModal()" style="flex: 1; padding: 8px; font-size: 11px; font-weight: 500; border: 1px solid #e5e5e5; border-radius: 4px; cursor: pointer; background: white; color: #374151;" title="Restore from CSS variables">
2383
+ Restore
2384
+ </button>
2385
+ <button @click="resetConfigToDefaults()" style="flex: 1; padding: 8px; font-size: 11px; font-weight: 500; border: 1px solid #fca5a5; border-radius: 4px; cursor: pointer; background: #fef2f2; color: #dc2626;" title="Reset to defaults">
2386
+ Reset
2387
+ </button>
2388
+ </div>
2389
+ <button @click="downloadCSS()" style="width: 100%; padding: 10px 12px; font-size: 12px; font-weight: 600; border: none; border-radius: 4px; cursor: pointer; background: #1a1a2e; color: white;">
2390
+ Download CSS
2372
2391
  </button>
2373
2392
  </div>
2374
2393
  </div>
@@ -2415,6 +2434,53 @@ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
2415
2434
  </div>
2416
2435
  </div>
2417
2436
 
2437
+ <!-- Close Warning Modal -->
2438
+ <div x-show="showCloseWarningModal"
2439
+ x-transition:enter="transition ease-out duration-200"
2440
+ x-transition:enter-start="opacity-0"
2441
+ x-transition:enter-end="opacity-100"
2442
+ x-transition:leave="transition ease-in duration-150"
2443
+ x-transition:leave-start="opacity-100"
2444
+ x-transition:leave-end="opacity-0"
2445
+ 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;">
2446
+ <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;">
2447
+ <!-- Modal Header -->
2448
+ <div style="padding: 12px 16px; background: #fef3c7; color: #92400e; border-radius: 8px 8px 0 0; display: flex; align-items: center; gap: 10px;">
2449
+ <svg style="width: 20px; height: 20px; flex-shrink: 0;" fill="currentColor" viewBox="0 0 20 20">
2450
+ <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"/>
2451
+ </svg>
2452
+ <span style="font-weight: 600; font-size: 14px;">Unsaved Configuration</span>
2453
+ </div>
2454
+ <!-- Modal Body -->
2455
+ <div style="padding: 16px;">
2456
+ <p style="font-size: 13px; color: #374151; margin: 0 0 12px 0; line-height: 1.5;">
2457
+ Your grid configuration changes are stored in <strong>browser localStorage</strong> and will only apply while the visualizer is loaded.
2458
+ </p>
2459
+ <div style="background: #f3f4f6; border-radius: 6px; padding: 12px; margin-bottom: 12px;">
2460
+ <p style="font-size: 12px; color: #4b5563; margin: 0 0 8px 0; line-height: 1.5;">
2461
+ <strong>To make changes permanent:</strong>
2462
+ </p>
2463
+ <ol style="font-size: 11px; color: #6b7280; margin: 0; padding-left: 16px; line-height: 1.6;">
2464
+ <li>Copy the config to your clipboard</li>
2465
+ <li>Paste into your project's CSS file</li>
2466
+ <li>Remove the visualizer script from production</li>
2467
+ </ol>
2468
+ </div>
2469
+ <p style="font-size: 11px; color: #9ca3af; margin: 0; line-height: 1.4;">
2470
+ Without copying, your changes will be lost when the visualizer is removed.
2471
+ </p>
2472
+ </div>
2473
+ <!-- Modal Footer -->
2474
+ <div style="padding: 12px 16px; background: #f7f7f7; border-radius: 0 0 8px 8px; display: flex; justify-content: space-between; gap: 8px;">
2475
+ <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>
2476
+ <div style="display: flex; gap: 8px;">
2477
+ <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>
2478
+ <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>
2479
+ </div>
2480
+ </div>
2481
+ </div>
2482
+ </div>
2483
+
2418
2484
  <!-- Grid Diagram -->
2419
2485
  <div x-show="showDiagram"
2420
2486
  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.0
3
+ * Version: 5.1.2
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.0",
3
+ "version": "5.1.2",
4
4
  "type": "module",
5
5
  "description": "CSS Grid breakout layout system with visual configurator",
6
6
  "main": "dist/_objects.breakout-grid.css",
@@ -57,9 +57,8 @@
57
57
  "check-version": "node scripts/check-version.js",
58
58
  "prepublishOnly": "npm run build && npm run check-files",
59
59
  "check-files": "npm pack --dry-run",
60
- "release": "npm run build && npm run check-files",
61
- "release:patch": "npm run release && npm version patch && git push && git push --tags",
62
- "release:minor": "npm run release && npm version minor && git push && git push --tags",
63
- "release:major": "npm run release && npm version major && git push && git push --tags"
60
+ "release:patch": "npm version patch --no-git-tag-version && npm run build && npm run check-version && git add -A && git commit -m \"$(node -p 'require(`./package.json`).version')\" && git tag \"v$(node -p 'require(`./package.json`).version')\" && git push && git push --tags",
61
+ "release:minor": "npm version minor --no-git-tag-version && npm run build && npm run check-version && git add -A && git commit -m \"$(node -p 'require(`./package.json`).version')\" && git tag \"v$(node -p 'require(`./package.json`).version')\" && git push && git push --tags",
62
+ "release:major": "npm version major --no-git-tag-version && npm run build && npm run check-version && git add -A && git commit -m \"$(node -p 'require(`./package.json`).version')\" && git tag \"v$(node -p 'require(`./package.json`).version')\" && git push && git push --tags"
64
63
  }
65
64
  }