@vectoriox/iox-builder 1.4.3 → 1.4.4

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.
@@ -1294,177 +1294,541 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
1294
1294
  type: Injectable
1295
1295
  }], ctorParameters: () => [{ type: OverlayService }] });
1296
1296
 
1297
+ var StyleCategory;
1298
+ (function (StyleCategory) {
1299
+ StyleCategory["Layout"] = "Layout";
1300
+ StyleCategory["Size"] = "Size";
1301
+ StyleCategory["Typography"] = "Typography";
1302
+ StyleCategory["Spacing"] = "Spacing";
1303
+ StyleCategory["Border"] = "Border";
1304
+ StyleCategory["Position"] = "Position";
1305
+ StyleCategory["Background"] = "Background";
1306
+ StyleCategory["Effects"] = "Effects";
1307
+ // Legacy — kept for backward compat; prefer Size
1308
+ StyleCategory["Dimensions"] = "Dimensions";
1309
+ })(StyleCategory || (StyleCategory = {}));
1310
+ const TRIGGER_OPTIONS = [
1311
+ { value: 'pageLoad', label: 'Page load', icon: 'ph-thin ph-play' },
1312
+ { value: 'viewportEnter', label: 'Enter viewport', icon: 'ph-thin ph-eye' },
1313
+ { value: 'click', label: 'Click', icon: 'ph-thin ph-cursor-click' },
1314
+ { value: 'hover', label: 'Hover', icon: 'ph-thin ph-hand-pointing' },
1315
+ { value: 'scrollProgress', label: 'Scroll progress', icon: 'ph-thin ph-arrow-fat-lines-down' },
1316
+ ];
1317
+ const ACTION_TYPE_OPTIONS = [
1318
+ { value: 'fadeIn', label: 'Fade in' },
1319
+ { value: 'fadeOut', label: 'Fade out' },
1320
+ { value: 'moveUp', label: 'Move up' },
1321
+ { value: 'moveDown', label: 'Move down' },
1322
+ { value: 'moveLeft', label: 'Move left' },
1323
+ { value: 'moveRight', label: 'Move right' },
1324
+ { value: 'scaleIn', label: 'Scale in' },
1325
+ { value: 'scaleOut', label: 'Scale out' },
1326
+ { value: 'rotate', label: 'Rotate' },
1327
+ { value: 'show', label: 'Show' },
1328
+ { value: 'hide', label: 'Hide' },
1329
+ { value: 'toggleVisibility', label: 'Toggle visibility' },
1330
+ ];
1331
+ const EASING_OPTIONS = ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'];
1332
+ /** Interaction pseudo-classes — compiled to .iox-node-{id}:state */
1333
+ const INTERACTION_STATES = ['hover', 'active', 'focus'];
1334
+ /** Structural pseudo-classes — compiled to .iox-outer-{id}:state */
1335
+ const STRUCTURAL_STATES = ['first-child', 'last-child', 'nth-child(odd)', 'nth-child(even)'];
1336
+ const SUPPORTED_STATES = [...INTERACTION_STATES, ...STRUCTURAL_STATES];
1297
1337
  /**
1298
- * StyleRegistryService manages a single <style> tag for the builder canvas.
1299
- *
1300
- * Each builder component gets two CSS classes:
1301
- * .iox-node-{id} — applied to the inner element; handles layout/visual props
1302
- * (display, flex, background, border, padding, etc.)
1303
- * .iox-outer-{id} — applied to the host element by RenderDirective; handles
1304
- * properties that participate in the PARENT flow (margin,
1305
- * align-self, flex-grow/shrink/basis, order).
1306
- *
1307
- * This split is necessary because margin on an inner wrapper only adds internal
1308
- * space — it does not push sibling elements. Applying margin to the host element
1309
- * (which IS the flex/block child in the parent container) makes it work correctly.
1310
- *
1311
- * State styles (hover/active/focus) are stored under key `${nodeId}:${state}`
1312
- * and compiled to `.iox-node-{id}:hover { … }` selectors.
1313
- *
1314
- * Design token variables are stored under the reserved key `__tokens__` and
1315
- * compiled to a `:root { … }` block prepended to all other rules.
1316
- *
1317
- * Scoped to PageUiComponent.providers[] — one stylesheet per builder instance.
1338
+ * Build a full StyleTraitGroup[] schema populated with values from a flat
1339
+ * styleProps map. Used when editing a preset in the Style panel — the panel
1340
+ * receives the same `StyleTraitGroup[]` shape regardless of context.
1318
1341
  */
1319
- class StyleRegistryService {
1320
- constructor() {
1321
- this.changes$ = new Subject();
1322
- this.rules = new Map();
1323
- this.styleEl = null;
1324
- }
1325
- /** Properties that must live on the wrapper element to affect the parent layout. */
1326
- static { this.OUTER_PROPS = new Set([
1327
- 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
1328
- 'alignSelf', 'flexGrow', 'flexShrink', 'flexBasis', 'order',
1329
- 'width',
1330
- ]); }
1331
- init() {
1332
- this.styleEl = document.createElement('style');
1333
- this.styleEl.id = 'iox-runtime-styles';
1334
- document.head.appendChild(this.styleEl);
1342
+ function buildPresetStyleTraits(styleProps) {
1343
+ const allGroups = [
1344
+ new LayoutGroupStyleConfig(),
1345
+ new SizeGroupStyleConfig(),
1346
+ new BackgroundGroupStyleConfig(),
1347
+ new SpacingGroupStyleConfig(),
1348
+ new BorderGroupStyleConfig(),
1349
+ new TypographyGroupStyleConfig(),
1350
+ new EffectsGroupStyleConfig(),
1351
+ new PositionGroupStyleConfig(),
1352
+ ];
1353
+ return allGroups.map(group => ({
1354
+ ...group,
1355
+ traits: group.traits.map(trait => ({
1356
+ ...trait,
1357
+ default: styleProps[trait.name] !== undefined ? styleProps[trait.name] : trait.default,
1358
+ })),
1359
+ }));
1360
+ }
1361
+ var TraitInputType;
1362
+ (function (TraitInputType) {
1363
+ TraitInputType["Text"] = "text";
1364
+ TraitInputType["Number"] = "number";
1365
+ TraitInputType["Select"] = "select";
1366
+ TraitInputType["Checkbox"] = "checkbox";
1367
+ TraitInputType["Color"] = "colorPicker";
1368
+ TraitInputType["DirectionalSize"] = "directionalSize";
1369
+ TraitInputType["SelectButton"] = "selectButton";
1370
+ TraitInputType["Scrub"] = "scrub";
1371
+ TraitInputType["Icon"] = "icon";
1372
+ TraitInputType["Media"] = "media";
1373
+ TraitInputType["FontFamily"] = "fontFamily";
1374
+ TraitInputType["GridTemplate"] = "gridTemplate";
1375
+ })(TraitInputType || (TraitInputType = {}));
1376
+ function generateNodeId() {
1377
+ return Math.random().toString(36).substr(2, 9);
1378
+ }
1379
+ class ComponentConfig {
1380
+ constructor(type, selector, icon = 'ph-thin ph-cube', category = 'General') {
1381
+ this.id = generateNodeId();
1382
+ this.type = type;
1383
+ this.selector = selector;
1384
+ this.icon = icon;
1385
+ this.category = category;
1386
+ this.inputs = {};
1387
+ this.traits = [];
1388
+ this.styleTraits = [];
1335
1389
  }
1336
- /**
1337
- * Write or update the base styles for a node, or a pseudo-class state override.
1338
- *
1339
- * @param nodeId The node's CSS id (used in `.iox-node-{nodeId}`)
1340
- * @param styles Flat CSS property map (camelCase keys)
1341
- * @param state Optional pseudo-class state ('hover' | 'active' | 'focus').
1342
- * When provided, compiles to `.iox-node-{id}:{state} { … }`.
1343
- * State rules are NOT partitioned into inner/outer.
1344
- */
1345
- upsert(nodeId, styles, state) {
1346
- if (!nodeId)
1347
- return;
1348
- if (state) {
1349
- const css = this.compile(`iox-node-${nodeId}:${state}`, styles);
1350
- const key = `${nodeId}:${state}`;
1351
- if (css) {
1352
- this.rules.set(key, css);
1353
- }
1354
- else {
1355
- this.rules.delete(key);
1356
- }
1357
- this.flush();
1358
- return;
1359
- }
1360
- const inner = {};
1361
- const outer = {};
1362
- for (const [k, v] of Object.entries(styles)) {
1363
- if (StyleRegistryService.OUTER_PROPS.has(k)) {
1364
- outer[k] = v;
1365
- }
1366
- else {
1367
- inner[k] = v;
1390
+ /** Override specific trait defaults after styleTraits are set.
1391
+ * Call this at the end of each subclass constructor. */
1392
+ applyStyleDefaults(defaults) {
1393
+ for (const group of this.styleTraits) {
1394
+ for (const trait of group.traits) {
1395
+ if (defaults[trait.name] !== undefined) {
1396
+ trait.default = defaults[trait.name];
1397
+ }
1368
1398
  }
1369
1399
  }
1370
- const innerCss = this.compile(`iox-node-${nodeId}`, inner);
1371
- const outerCss = this.compile(`iox-outer-${nodeId}`, outer);
1372
- const combined = [innerCss, outerCss].filter(Boolean).join('\n');
1373
- if (combined) {
1374
- this.rules.set(nodeId, combined);
1375
- }
1376
- else {
1377
- this.rules.delete(nodeId);
1378
- }
1379
- this.flush();
1380
1400
  }
1381
- /**
1382
- * Write the org-level design token variables as a `:root { … }` block.
1383
- * Token names should be CSS custom property names without the `--` prefix
1384
- * (e.g., `{ 'iox-primary-color': '#cb9090' }`).
1385
- * Pass an empty object to clear all tokens.
1386
- */
1387
- upsertTokens(tokens) {
1388
- const entries = Object.entries(tokens)
1389
- .filter(([, v]) => v !== undefined && v !== null && v !== '')
1390
- .map(([k, v]) => ` --${k}: ${v};`);
1391
- if (entries.length) {
1392
- this.rules.set('__tokens__', `:root {\n${entries.join('\n')}\n}`);
1393
- }
1394
- else {
1395
- this.rules.delete('__tokens__');
1396
- }
1397
- this.flush();
1401
+ }
1402
+ class TraitConfig {
1403
+ constructor(name, label, type, options, defaultValue, inline, showWhen) {
1404
+ this.name = name;
1405
+ this.label = label;
1406
+ this.type = type;
1407
+ this.options = options;
1408
+ this.default = defaultValue;
1409
+ this.inline = inline;
1410
+ this.showWhen = showWhen;
1398
1411
  }
1399
- remove(nodeId) {
1400
- if (!nodeId)
1401
- return;
1402
- // Remove base rule and all state rules for this node
1403
- this.rules.delete(nodeId);
1404
- for (const key of this.rules.keys()) {
1405
- if (key.startsWith(`${nodeId}:`)) {
1406
- this.rules.delete(key);
1407
- }
1408
- }
1409
- this.flush();
1412
+ }
1413
+ class GroupStyleConfig {
1414
+ constructor(category, traits) {
1415
+ this.category = category;
1416
+ this.traits = traits;
1410
1417
  }
1411
- destroy() {
1412
- this.rules.clear();
1413
- if (this.styleEl) {
1414
- this.styleEl.remove();
1415
- this.styleEl = null;
1416
- }
1418
+ }
1419
+ // ─── Unit constants ───────────────────────────────────────────────────────────
1420
+ const UNITS_ALL = ['px', '%', 'rem', 'em', 'vw', 'vh'];
1421
+ const UNITS_NO_VW = ['px', '%', 'rem', 'em', 'vh'];
1422
+ const UNITS_FIXED = ['px', '%', 'rem', 'em'];
1423
+ const UNITS_DEG = ['deg'];
1424
+ // ─── Shared panel utilities ──────────────────────────────────────────────────
1425
+ function resolveTraitControllerType(type) {
1426
+ switch (type) {
1427
+ case TraitInputType.Number: return 'number';
1428
+ case TraitInputType.Select: return 'select';
1429
+ case TraitInputType.Checkbox: return 'switch';
1430
+ case TraitInputType.Color: return 'colorPicker';
1431
+ case TraitInputType.DirectionalSize: return 'directionalSize';
1432
+ case TraitInputType.SelectButton: return 'selectButton';
1433
+ case TraitInputType.Scrub: return 'scrub';
1434
+ case TraitInputType.Icon: return 'icon';
1435
+ case TraitInputType.Media: return 'media';
1436
+ case TraitInputType.FontFamily: return 'select';
1437
+ case TraitInputType.GridTemplate: return 'gridTemplate';
1438
+ default: return 'text';
1417
1439
  }
1418
- compile(className, styles) {
1419
- const entries = Object.entries(styles)
1420
- .filter(([, v]) => v !== undefined && v !== null && v !== '')
1421
- .map(([k, v]) => ` ${this.toKebabCase(k)}: ${v};`);
1422
- if (!entries.length)
1423
- return '';
1424
- return `.${className} {\n${entries.join('\n')}\n}`;
1440
+ }
1441
+ function resolveTraitOptions(trait) {
1442
+ if (trait.type === TraitInputType.Color ||
1443
+ trait.type === TraitInputType.DirectionalSize ||
1444
+ trait.type === TraitInputType.SelectButton ||
1445
+ trait.type === TraitInputType.Scrub ||
1446
+ trait.type === TraitInputType.Media) {
1447
+ return trait.options;
1425
1448
  }
1426
- toKebabCase(str) {
1427
- return str.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
1449
+ if (trait.type === TraitInputType.FontFamily) {
1450
+ // FontFamily options are injected dynamically by the style panel via FontManagerService.
1451
+ // Return empty array here; the panel overrides this in resolveOptions().
1452
+ return [];
1428
1453
  }
1429
- flush() {
1430
- if (!this.styleEl)
1431
- return;
1432
- // __tokens__ always first so variables are available to all subsequent rules
1433
- const tokenBlock = this.rules.get('__tokens__');
1434
- const rest = Array.from(this.rules.entries())
1435
- .filter(([k]) => k !== '__tokens__')
1436
- .map(([, v]) => v);
1437
- this.styleEl.textContent = [tokenBlock, ...rest].filter(Boolean).join('\n');
1438
- this.changes$.next();
1454
+ if (trait.type !== TraitInputType.Select) {
1455
+ return undefined;
1439
1456
  }
1440
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StyleRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1441
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StyleRegistryService }); }
1457
+ const opts = trait.options ?? [];
1458
+ // Already formatted as {label, value}[]
1459
+ if (opts.length > 0 && typeof opts[0] === 'object' && 'label' in opts[0]) {
1460
+ return opts;
1461
+ }
1462
+ return opts.map((option) => ({ label: option, value: option }));
1463
+ }
1464
+ // ─── Style Groups ─────────────────────────────────────────────────────────────
1465
+ const NON_STATIC_POSITIONS = ['relative', 'absolute', 'fixed', 'sticky'];
1466
+ class PositionGroupStyleConfig extends GroupStyleConfig {
1467
+ constructor() {
1468
+ super(StyleCategory.Position, [
1469
+ new TraitConfig('position', 'Position', TraitInputType.Select, ['static', 'relative', 'absolute', 'fixed', 'sticky'], 'static'),
1470
+ new TraitConfig('top', 'T', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
1471
+ new TraitConfig('right', 'R', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
1472
+ new TraitConfig('bottom', 'B', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
1473
+ new TraitConfig('left', 'L', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
1474
+ new TraitConfig('zIndex', 'Z', TraitInputType.Scrub, { min: -99, max: 9999, step: 1, units: [''] }, '0', false, { trait: 'position', values: NON_STATIC_POSITIONS }),
1475
+ ]);
1476
+ }
1477
+ }
1478
+ // ─── Layout ───────────────────────────────────────────────────────────────────
1479
+ class LayoutGroupStyleConfig extends GroupStyleConfig {
1480
+ constructor() {
1481
+ super(StyleCategory.Layout, [
1482
+ new TraitConfig('display', 'Display', TraitInputType.Select, ['block', 'inline', 'inline-block', 'flex', 'grid'], 'block'),
1483
+ // ── Flex-only ─────────────────────────────────────────────────────
1484
+ new TraitConfig('flexDirection', 'Direction', TraitInputType.SelectButton, [
1485
+ { value: 'row', label: 'Horizontal', icon: 'ph-thin ph-arrow-right' },
1486
+ { value: 'row-reverse', label: 'Horizontal Reversed', icon: 'ph-thin ph-arrow-left' },
1487
+ { value: 'column', label: 'Vertical', icon: 'ph-thin ph-arrow-down' },
1488
+ { value: 'column-reverse', label: 'Vertical Reversed', icon: 'ph-thin ph-arrow-up' },
1489
+ ], 'row', false, { trait: 'display', values: 'flex' }),
1490
+ new TraitConfig('flexWrap', 'Flex Wrap', TraitInputType.SelectButton, [
1491
+ { value: 'nowrap', label: 'No Wrap', icon: 'ph-thin ph-minus' },
1492
+ { value: 'wrap', label: 'Wrap', icon: 'ph-thin ph-arrow-elbow-down-right' },
1493
+ { value: 'wrap-reverse', label: 'Wrap Reversed', icon: 'ph-thin ph-arrow-elbow-up-right' },
1494
+ ], 'nowrap', false, { trait: 'display', values: 'flex' }),
1495
+ // ── Shared flex + grid ────────────────────────────────────────────
1496
+ new TraitConfig('justifyContent', 'Justify', TraitInputType.SelectButton, [
1497
+ { value: 'flex-start', label: 'Start', icon: 'ph-thin ph-align-left' },
1498
+ { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-horizontal' },
1499
+ { value: 'flex-end', label: 'End', icon: 'ph-thin ph-align-right' },
1500
+ { value: 'space-between', label: 'Space Between', icon: 'ph-thin ph-distribute-horizontal' },
1501
+ { value: 'space-around', label: 'Space Around', icon: 'ph-thin ph-dots-three' },
1502
+ { value: 'space-evenly', label: 'Space Evenly', icon: 'ph-thin ph-dots-six' },
1503
+ ], 'flex-start', false, { trait: 'display', values: ['flex', 'grid'] }),
1504
+ new TraitConfig('alignItems', 'Align Items', TraitInputType.SelectButton, [
1505
+ { value: 'stretch', label: 'Stretch', icon: 'ph-thin ph-arrows-vertical' },
1506
+ { value: 'start', label: 'Start', icon: 'ph-thin ph-align-top' },
1507
+ { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-vertical' },
1508
+ { value: 'end', label: 'End', icon: 'ph-thin ph-align-bottom' },
1509
+ ], 'stretch', false, { trait: 'display', values: ['flex', 'grid'] }),
1510
+ new TraitConfig('gap', 'Gap', TraitInputType.Scrub, { min: 0, max: 200, step: 1, units: UNITS_FIXED }, '0px', false, { trait: 'display', values: ['flex', 'grid'] }),
1511
+ // ── Grid-only ─────────────────────────────────────────────────────
1512
+ new TraitConfig('gridTemplateColumns', 'Columns', TraitInputType.GridTemplate, undefined, '1fr', false, { trait: 'display', values: 'grid' }),
1513
+ new TraitConfig('gridTemplateRows', 'Rows', TraitInputType.GridTemplate, undefined, 'auto', false, { trait: 'display', values: 'grid' }),
1514
+ new TraitConfig('justifyItems', 'Justify Items', TraitInputType.SelectButton, [
1515
+ { value: 'stretch', label: 'Stretch', icon: 'ph-thin ph-arrows-horizontal' },
1516
+ { value: 'start', label: 'Start', icon: 'ph-thin ph-align-left' },
1517
+ { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-horizontal' },
1518
+ { value: 'end', label: 'End', icon: 'ph-thin ph-align-right' },
1519
+ ], 'stretch', false, { trait: 'display', values: 'grid' }),
1520
+ new TraitConfig('alignContent', 'Align Content', TraitInputType.SelectButton, [
1521
+ { value: 'start', label: 'Start', icon: 'ph-thin ph-align-top' },
1522
+ { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-vertical' },
1523
+ { value: 'end', label: 'End', icon: 'ph-thin ph-align-bottom' },
1524
+ { value: 'space-between', label: 'Space Between', icon: 'ph-thin ph-distribute-vertical' },
1525
+ { value: 'space-around', label: 'Space Around', icon: 'ph-thin ph-dots-three' },
1526
+ { value: 'stretch', label: 'Stretch', icon: 'ph-thin ph-arrows-vertical' },
1527
+ ], 'stretch', false, { trait: 'display', values: 'grid' }),
1528
+ // ── Direction (always visible) ────────────────────────────────────
1529
+ new TraitConfig('direction', 'Text Direction', TraitInputType.SelectButton, [
1530
+ { value: 'ltr', label: 'LTR' },
1531
+ { value: 'rtl', label: 'RTL' },
1532
+ ], 'ltr'),
1533
+ ]);
1534
+ }
1535
+ }
1536
+ // ─── Size (replaces Dimensions) ───────────────────────────────────────────────
1537
+ class SizeGroupStyleConfig extends GroupStyleConfig {
1538
+ constructor() {
1539
+ super(StyleCategory.Size, [
1540
+ new TraitConfig('width', 'W', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
1541
+ new TraitConfig('height', 'H', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
1542
+ new TraitConfig('minWidth', 'Min W', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
1543
+ new TraitConfig('minHeight', 'Min H', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
1544
+ new TraitConfig('maxWidth', 'Max W', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
1545
+ new TraitConfig('maxHeight', 'Max H', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
1546
+ new TraitConfig('aspectRatio', 'Aspect Ratio', TraitInputType.Text, undefined, 'auto'),
1547
+ ]);
1548
+ }
1549
+ }
1550
+ /** @deprecated Use SizeGroupStyleConfig instead. */
1551
+ class DimensionsGroupStyleConfig extends SizeGroupStyleConfig {
1552
+ }
1553
+ // ─── Background ───────────────────────────────────────────────────────────────
1554
+ class BackgroundGroupStyleConfig extends GroupStyleConfig {
1555
+ constructor() {
1556
+ super(StyleCategory.Background, [
1557
+ new TraitConfig('backgroundColor', 'Background', TraitInputType.Color, { allowGradient: true, allowTransparent: true, formats: ['hex', 'rgb', 'hsl', 'transparent'] }, 'transparent'),
1558
+ ]);
1559
+ }
1560
+ }
1561
+ class SpacingGroupStyleConfig extends GroupStyleConfig {
1562
+ constructor() {
1563
+ super(StyleCategory.Spacing, [
1564
+ new TraitConfig('margin', 'Margin', TraitInputType.DirectionalSize, {
1565
+ allowNegative: true,
1566
+ allowLinkedSlider: true,
1567
+ slider: { min: 0, max: 200, step: 1 }
1568
+ }, '0px 0px 0px 0px'),
1569
+ new TraitConfig('padding', 'Padding', TraitInputType.DirectionalSize, {
1570
+ allowNegative: false,
1571
+ allowLinkedSlider: true,
1572
+ slider: { min: 0, max: 200, step: 1 }
1573
+ }, '0px 0px 0px 0px')
1574
+ ]);
1575
+ }
1576
+ }
1577
+ class BorderGroupStyleConfig extends GroupStyleConfig {
1578
+ constructor() {
1579
+ super(StyleCategory.Border, [
1580
+ new TraitConfig('borderWidth', 'Border Width', TraitInputType.DirectionalSize, {
1581
+ units: UNITS_FIXED,
1582
+ allowNegative: false,
1583
+ allowLinkedSlider: true,
1584
+ slider: { min: 0, max: 32, step: 1 }
1585
+ }, '0px'),
1586
+ new TraitConfig('borderStyle', 'Border Style', TraitInputType.SelectButton, [
1587
+ { value: 'solid', icon: 'ph-thin ph-line-segment' },
1588
+ { value: 'dashed', icon: 'ph-thin ph-dots-three' },
1589
+ { value: 'dotted', icon: 'ph-thin ph-dots-six' },
1590
+ ], 'solid'),
1591
+ new TraitConfig('borderColor', 'Border Color', TraitInputType.Color, { allowGradient: false, allowTransparent: false, formats: ['hex', 'rgb', 'hsl'] }, '#CCCCCC'),
1592
+ new TraitConfig('borderRadius', 'Border Radius', TraitInputType.DirectionalSize, {
1593
+ units: UNITS_FIXED,
1594
+ allowNegative: false,
1595
+ allowLinkedSlider: true,
1596
+ linkByDefault: true,
1597
+ slider: { min: 0, max: 120, step: 1 },
1598
+ segments: [
1599
+ { key: 'top-left', icon: 'ph-arrow-bend-up-left', ariaLabel: 'top left radius' },
1600
+ { key: 'top-right', icon: 'ph-arrow-bend-up-right', ariaLabel: 'top right radius' },
1601
+ { key: 'bottom-right', icon: 'ph-arrow-bend-down-right', ariaLabel: 'bottom right radius' },
1602
+ { key: 'bottom-left', icon: 'ph-arrow-bend-down-left', ariaLabel: 'bottom left radius' }
1603
+ ]
1604
+ }, '0px')
1605
+ ]);
1606
+ }
1607
+ }
1608
+ class TypographyGroupStyleConfig extends GroupStyleConfig {
1609
+ constructor() {
1610
+ super(StyleCategory.Typography, [
1611
+ new TraitConfig('fontFamily', 'Font Family', TraitInputType.FontFamily, [], 'Poppins, sans-serif'),
1612
+ new TraitConfig('fontSize', 'Font Size', TraitInputType.Scrub, { min: 8, max: 200, step: 1, units: UNITS_FIXED }, '16px'),
1613
+ new TraitConfig('fontWeight', 'Font Weight', TraitInputType.Select, ['100', '200', '300', '400', '500', '600', '700', '800', '900'], '400'),
1614
+ new TraitConfig('color', 'Color', TraitInputType.Color, { allowGradient: false, allowTransparent: false, formats: ['hex', 'rgb', 'hsl'] }, '#000000'),
1615
+ new TraitConfig('lineHeight', 'Line Height', TraitInputType.Select, ['1', '1.2', '1.4', '1.5', '1.75', '2', '2.5'], '1.5'),
1616
+ new TraitConfig('letterSpacing', 'Letter Spacing', TraitInputType.Scrub, { min: -5, max: 50, step: 0.5, units: UNITS_FIXED }, '0px'),
1617
+ new TraitConfig('textAlign', 'Alignment', TraitInputType.SelectButton, [
1618
+ { value: 'left', icon: 'ph-thin ph-text-align-left' },
1619
+ { value: 'center', icon: 'ph-thin ph-text-align-center' },
1620
+ { value: 'right', icon: 'ph-thin ph-text-align-right' },
1621
+ { value: 'justify', icon: 'ph-thin ph-text-align-justify' },
1622
+ ], 'left'),
1623
+ new TraitConfig('fontStyle', 'Italic', TraitInputType.SelectButton, { items: [{ value: 'italic', icon: 'ph-thin ph-text-italic', label: 'Italic' }], allowEmpty: true, noneValue: 'normal' }, 'normal', true),
1624
+ new TraitConfig('textDecoration', 'Underline', TraitInputType.SelectButton, { items: [{ value: 'underline', icon: 'ph-thin ph-text-underline', label: 'Underline' }], allowEmpty: true, noneValue: 'none' }, 'none', true),
1625
+ ]);
1626
+ }
1627
+ }
1628
+ class EffectsGroupStyleConfig extends GroupStyleConfig {
1629
+ constructor() {
1630
+ super(StyleCategory.Effects, [
1631
+ new TraitConfig('opacity', 'Opacity', TraitInputType.Scrub, { min: 0, max: 1, step: 0.01, units: [''] }, '1'),
1632
+ new TraitConfig('mixBlendMode', 'Blend Mode', TraitInputType.Select, [
1633
+ 'normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten',
1634
+ 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
1635
+ 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity',
1636
+ ], 'normal'),
1637
+ new TraitConfig('cursor', 'Cursor', TraitInputType.Select, [
1638
+ 'auto', 'default', 'pointer', 'text', 'move', 'not-allowed',
1639
+ 'grab', 'grabbing', 'crosshair', 'zoom-in', 'zoom-out', 'none',
1640
+ ], 'auto'),
1641
+ ]);
1642
+ }
1643
+ }
1644
+ function flattenSchemaFields(properties, prefix = '') {
1645
+ const result = [];
1646
+ for (const [key, field] of Object.entries(properties)) {
1647
+ const fullPath = prefix ? `${prefix}.${key}` : key;
1648
+ if (field.type === 'object' && field.properties) {
1649
+ result.push(...flattenSchemaFields(field.properties, fullPath));
1650
+ }
1651
+ else {
1652
+ result.push({ label: fullPath, value: fullPath });
1653
+ }
1654
+ }
1655
+ return result;
1442
1656
  }
1443
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StyleRegistryService, decorators: [{
1444
- type: Injectable
1445
- }] });
1446
1657
 
1447
- // Module-level counter — unique drop-list ID per Section instance across the whole app session.
1448
- let dropListCounter = 0;
1449
1658
  /**
1450
- * RenderDirectiveapplied to <ng-container> to dynamically instantiate a
1451
- * builder component with zero wrapper elements in the DOM.
1659
+ * StyleRegistryServicemanages a single <style> tag for the builder canvas.
1452
1660
  *
1453
- * A directive's ViewContainerRef inserts the created component's host element as
1454
- * a sibling of the ng-container comment node directly inside the parent element
1455
- * so the resulting DOM has no extra <app-renderer> wrapper.
1661
+ * Each builder component gets two CSS classes:
1662
+ * .iox-node-{id} applied to the inner element; handles layout/visual props
1663
+ * (display, flex, background, border, padding, etc.)
1664
+ * .iox-outer-{id} — applied to the host element by RenderDirective; handles
1665
+ * properties that participate in the PARENT flow (margin,
1666
+ * align-self, flex-grow/shrink/basis, order).
1456
1667
  *
1457
- * Usage:
1458
- * <ng-container [ioxRender]="node"
1459
- * (onClick)="handleClick($event)"
1460
- * (onMouseEnter)="handleMouseEnter($event)"
1461
- * (onMouseLeave)="handleMouseLeave()">
1462
- * </ng-container>
1668
+ * This split is necessary because margin on an inner wrapper only adds internal
1669
+ * space — it does not push sibling elements. Applying margin to the host element
1670
+ * (which IS the flex/block child in the parent container) makes it work correctly.
1671
+ *
1672
+ * State styles (hover/active/focus) are stored under key `${nodeId}:${state}`
1673
+ * and compiled to `.iox-node-{id}:hover { … }` selectors.
1674
+ *
1675
+ * Design token variables are stored under the reserved key `__tokens__` and
1676
+ * compiled to a `:root { … }` block prepended to all other rules.
1677
+ *
1678
+ * Scoped to PageUiComponent.providers[] — one stylesheet per builder instance.
1463
1679
  */
1464
- class RenderDirective {
1465
- constructor(vcr, injector, registryService, overlayService, styleRegistry, interactionEngine, cdr, panelEventService) {
1466
- this.vcr = vcr;
1467
- this.injector = injector;
1680
+ class StyleRegistryService {
1681
+ constructor() {
1682
+ this.changes$ = new Subject();
1683
+ this.rules = new Map();
1684
+ this.styleEl = null;
1685
+ }
1686
+ /** Properties that must live on the wrapper element to affect the parent layout. */
1687
+ static { this.OUTER_PROPS = new Set([
1688
+ 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
1689
+ 'alignSelf', 'flexGrow', 'flexShrink', 'flexBasis', 'order',
1690
+ 'width',
1691
+ ]); }
1692
+ init() {
1693
+ this.styleEl = document.createElement('style');
1694
+ this.styleEl.id = 'iox-runtime-styles';
1695
+ document.head.appendChild(this.styleEl);
1696
+ }
1697
+ /**
1698
+ * Write or update the base styles for a node, or a pseudo-class state override.
1699
+ *
1700
+ * @param nodeId The node's CSS id (used in `.iox-node-{nodeId}`)
1701
+ * @param styles Flat CSS property map (camelCase keys)
1702
+ * @param state Optional pseudo-class state ('hover' | 'active' | 'focus').
1703
+ * When provided, compiles to `.iox-node-{id}:{state} { … }`.
1704
+ * State rules are NOT partitioned into inner/outer.
1705
+ */
1706
+ upsert(nodeId, styles, state) {
1707
+ if (!nodeId)
1708
+ return;
1709
+ if (state) {
1710
+ // Structural pseudo-classes (first-child, last-child, nth-child) target the
1711
+ // outer wrapper element so they participate correctly in grid/flex layout.
1712
+ const prefix = STRUCTURAL_STATES.includes(state) ? 'iox-outer' : 'iox-node';
1713
+ const css = this.compile(`${prefix}-${nodeId}:${state}`, styles);
1714
+ const key = `${nodeId}:${state}`;
1715
+ if (css) {
1716
+ this.rules.set(key, css);
1717
+ }
1718
+ else {
1719
+ this.rules.delete(key);
1720
+ }
1721
+ this.flush();
1722
+ return;
1723
+ }
1724
+ const inner = {};
1725
+ const outer = {};
1726
+ for (const [k, v] of Object.entries(styles)) {
1727
+ if (StyleRegistryService.OUTER_PROPS.has(k)) {
1728
+ outer[k] = v;
1729
+ }
1730
+ else {
1731
+ inner[k] = v;
1732
+ }
1733
+ }
1734
+ const innerCss = this.compile(`iox-node-${nodeId}`, inner);
1735
+ const outerCss = this.compile(`iox-outer-${nodeId}`, outer);
1736
+ const combined = [innerCss, outerCss].filter(Boolean).join('\n');
1737
+ if (combined) {
1738
+ this.rules.set(nodeId, combined);
1739
+ }
1740
+ else {
1741
+ this.rules.delete(nodeId);
1742
+ }
1743
+ this.flush();
1744
+ }
1745
+ /**
1746
+ * Write the org-level design token variables as a `:root { … }` block.
1747
+ * Token names should be CSS custom property names without the `--` prefix
1748
+ * (e.g., `{ 'iox-primary-color': '#cb9090' }`).
1749
+ * Pass an empty object to clear all tokens.
1750
+ */
1751
+ upsertTokens(tokens) {
1752
+ const entries = Object.entries(tokens)
1753
+ .filter(([, v]) => v !== undefined && v !== null && v !== '')
1754
+ .map(([k, v]) => ` --${k}: ${v};`);
1755
+ if (entries.length) {
1756
+ this.rules.set('__tokens__', `:root {\n${entries.join('\n')}\n}`);
1757
+ }
1758
+ else {
1759
+ this.rules.delete('__tokens__');
1760
+ }
1761
+ this.flush();
1762
+ }
1763
+ remove(nodeId) {
1764
+ if (!nodeId)
1765
+ return;
1766
+ // Remove base rule and all state rules for this node
1767
+ this.rules.delete(nodeId);
1768
+ for (const key of this.rules.keys()) {
1769
+ if (key.startsWith(`${nodeId}:`)) {
1770
+ this.rules.delete(key);
1771
+ }
1772
+ }
1773
+ this.flush();
1774
+ }
1775
+ destroy() {
1776
+ this.rules.clear();
1777
+ if (this.styleEl) {
1778
+ this.styleEl.remove();
1779
+ this.styleEl = null;
1780
+ }
1781
+ }
1782
+ compile(className, styles) {
1783
+ const entries = Object.entries(styles)
1784
+ .filter(([, v]) => v !== undefined && v !== null && v !== '')
1785
+ .map(([k, v]) => ` ${this.toKebabCase(k)}: ${v};`);
1786
+ if (!entries.length)
1787
+ return '';
1788
+ return `.${className} {\n${entries.join('\n')}\n}`;
1789
+ }
1790
+ toKebabCase(str) {
1791
+ return str.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
1792
+ }
1793
+ flush() {
1794
+ if (!this.styleEl)
1795
+ return;
1796
+ // __tokens__ always first so variables are available to all subsequent rules
1797
+ const tokenBlock = this.rules.get('__tokens__');
1798
+ const rest = Array.from(this.rules.entries())
1799
+ .filter(([k]) => k !== '__tokens__')
1800
+ .map(([, v]) => v);
1801
+ this.styleEl.textContent = [tokenBlock, ...rest].filter(Boolean).join('\n');
1802
+ this.changes$.next();
1803
+ }
1804
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StyleRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1805
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StyleRegistryService }); }
1806
+ }
1807
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StyleRegistryService, decorators: [{
1808
+ type: Injectable
1809
+ }] });
1810
+
1811
+ // Module-level counter — unique drop-list ID per Section instance across the whole app session.
1812
+ let dropListCounter = 0;
1813
+ /**
1814
+ * RenderDirective — applied to <ng-container> to dynamically instantiate a
1815
+ * builder component with zero wrapper elements in the DOM.
1816
+ *
1817
+ * A directive's ViewContainerRef inserts the created component's host element as
1818
+ * a sibling of the ng-container comment node — directly inside the parent element
1819
+ * — so the resulting DOM has no extra <app-renderer> wrapper.
1820
+ *
1821
+ * Usage:
1822
+ * <ng-container [ioxRender]="node"
1823
+ * (onClick)="handleClick($event)"
1824
+ * (onMouseEnter)="handleMouseEnter($event)"
1825
+ * (onMouseLeave)="handleMouseLeave()">
1826
+ * </ng-container>
1827
+ */
1828
+ class RenderDirective {
1829
+ constructor(vcr, injector, registryService, overlayService, styleRegistry, interactionEngine, cdr, panelEventService) {
1830
+ this.vcr = vcr;
1831
+ this.injector = injector;
1468
1832
  this.registryService = registryService;
1469
1833
  this.overlayService = overlayService;
1470
1834
  this.styleRegistry = styleRegistry;
@@ -3825,363 +4189,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
3825
4189
  args: [PanelChildComponent]
3826
4190
  }] } });
3827
4191
 
3828
- var StyleCategory;
3829
- (function (StyleCategory) {
3830
- StyleCategory["Layout"] = "Layout";
3831
- StyleCategory["Size"] = "Size";
3832
- StyleCategory["Typography"] = "Typography";
3833
- StyleCategory["Spacing"] = "Spacing";
3834
- StyleCategory["Border"] = "Border";
3835
- StyleCategory["Position"] = "Position";
3836
- StyleCategory["Background"] = "Background";
3837
- StyleCategory["Effects"] = "Effects";
3838
- // Legacy — kept for backward compat; prefer Size
3839
- StyleCategory["Dimensions"] = "Dimensions";
3840
- })(StyleCategory || (StyleCategory = {}));
3841
- const TRIGGER_OPTIONS = [
3842
- { value: 'pageLoad', label: 'Page load', icon: 'ph-thin ph-play' },
3843
- { value: 'viewportEnter', label: 'Enter viewport', icon: 'ph-thin ph-eye' },
3844
- { value: 'click', label: 'Click', icon: 'ph-thin ph-cursor-click' },
3845
- { value: 'hover', label: 'Hover', icon: 'ph-thin ph-hand-pointing' },
3846
- { value: 'scrollProgress', label: 'Scroll progress', icon: 'ph-thin ph-arrow-fat-lines-down' },
3847
- ];
3848
- const ACTION_TYPE_OPTIONS = [
3849
- { value: 'fadeIn', label: 'Fade in' },
3850
- { value: 'fadeOut', label: 'Fade out' },
3851
- { value: 'moveUp', label: 'Move up' },
3852
- { value: 'moveDown', label: 'Move down' },
3853
- { value: 'moveLeft', label: 'Move left' },
3854
- { value: 'moveRight', label: 'Move right' },
3855
- { value: 'scaleIn', label: 'Scale in' },
3856
- { value: 'scaleOut', label: 'Scale out' },
3857
- { value: 'rotate', label: 'Rotate' },
3858
- { value: 'show', label: 'Show' },
3859
- { value: 'hide', label: 'Hide' },
3860
- { value: 'toggleVisibility', label: 'Toggle visibility' },
3861
- ];
3862
- const EASING_OPTIONS = ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'];
3863
- const SUPPORTED_STATES = ['hover', 'active', 'focus'];
3864
- /**
3865
- * Build a full StyleTraitGroup[] schema populated with values from a flat
3866
- * styleProps map. Used when editing a preset in the Style panel — the panel
3867
- * receives the same `StyleTraitGroup[]` shape regardless of context.
3868
- */
3869
- function buildPresetStyleTraits(styleProps) {
3870
- const allGroups = [
3871
- new LayoutGroupStyleConfig(),
3872
- new SizeGroupStyleConfig(),
3873
- new BackgroundGroupStyleConfig(),
3874
- new SpacingGroupStyleConfig(),
3875
- new BorderGroupStyleConfig(),
3876
- new TypographyGroupStyleConfig(),
3877
- new EffectsGroupStyleConfig(),
3878
- new PositionGroupStyleConfig(),
3879
- ];
3880
- return allGroups.map(group => ({
3881
- ...group,
3882
- traits: group.traits.map(trait => ({
3883
- ...trait,
3884
- default: styleProps[trait.name] !== undefined ? styleProps[trait.name] : trait.default,
3885
- })),
3886
- }));
3887
- }
3888
- var TraitInputType;
3889
- (function (TraitInputType) {
3890
- TraitInputType["Text"] = "text";
3891
- TraitInputType["Number"] = "number";
3892
- TraitInputType["Select"] = "select";
3893
- TraitInputType["Checkbox"] = "checkbox";
3894
- TraitInputType["Color"] = "colorPicker";
3895
- TraitInputType["DirectionalSize"] = "directionalSize";
3896
- TraitInputType["SelectButton"] = "selectButton";
3897
- TraitInputType["Scrub"] = "scrub";
3898
- TraitInputType["Icon"] = "icon";
3899
- TraitInputType["Media"] = "media";
3900
- TraitInputType["FontFamily"] = "fontFamily";
3901
- TraitInputType["GridTemplate"] = "gridTemplate";
3902
- })(TraitInputType || (TraitInputType = {}));
3903
- function generateNodeId() {
3904
- return Math.random().toString(36).substr(2, 9);
3905
- }
3906
- class ComponentConfig {
3907
- constructor(type, selector, icon = 'ph-thin ph-cube', category = 'General') {
3908
- this.id = generateNodeId();
3909
- this.type = type;
3910
- this.selector = selector;
3911
- this.icon = icon;
3912
- this.category = category;
3913
- this.inputs = {};
3914
- this.traits = [];
3915
- this.styleTraits = [];
3916
- }
3917
- /** Override specific trait defaults after styleTraits are set.
3918
- * Call this at the end of each subclass constructor. */
3919
- applyStyleDefaults(defaults) {
3920
- for (const group of this.styleTraits) {
3921
- for (const trait of group.traits) {
3922
- if (defaults[trait.name] !== undefined) {
3923
- trait.default = defaults[trait.name];
3924
- }
3925
- }
3926
- }
3927
- }
3928
- }
3929
- class TraitConfig {
3930
- constructor(name, label, type, options, defaultValue, inline, showWhen) {
3931
- this.name = name;
3932
- this.label = label;
3933
- this.type = type;
3934
- this.options = options;
3935
- this.default = defaultValue;
3936
- this.inline = inline;
3937
- this.showWhen = showWhen;
3938
- }
3939
- }
3940
- class GroupStyleConfig {
3941
- constructor(category, traits) {
3942
- this.category = category;
3943
- this.traits = traits;
3944
- }
3945
- }
3946
- // ─── Unit constants ───────────────────────────────────────────────────────────
3947
- const UNITS_ALL = ['px', '%', 'rem', 'em', 'vw', 'vh'];
3948
- const UNITS_NO_VW = ['px', '%', 'rem', 'em', 'vh'];
3949
- const UNITS_FIXED = ['px', '%', 'rem', 'em'];
3950
- const UNITS_DEG = ['deg'];
3951
- // ─── Shared panel utilities ──────────────────────────────────────────────────
3952
- function resolveTraitControllerType(type) {
3953
- switch (type) {
3954
- case TraitInputType.Number: return 'number';
3955
- case TraitInputType.Select: return 'select';
3956
- case TraitInputType.Checkbox: return 'switch';
3957
- case TraitInputType.Color: return 'colorPicker';
3958
- case TraitInputType.DirectionalSize: return 'directionalSize';
3959
- case TraitInputType.SelectButton: return 'selectButton';
3960
- case TraitInputType.Scrub: return 'scrub';
3961
- case TraitInputType.Icon: return 'icon';
3962
- case TraitInputType.Media: return 'media';
3963
- case TraitInputType.FontFamily: return 'select';
3964
- case TraitInputType.GridTemplate: return 'gridTemplate';
3965
- default: return 'text';
3966
- }
3967
- }
3968
- function resolveTraitOptions(trait) {
3969
- if (trait.type === TraitInputType.Color ||
3970
- trait.type === TraitInputType.DirectionalSize ||
3971
- trait.type === TraitInputType.SelectButton ||
3972
- trait.type === TraitInputType.Scrub ||
3973
- trait.type === TraitInputType.Media) {
3974
- return trait.options;
3975
- }
3976
- if (trait.type === TraitInputType.FontFamily) {
3977
- // FontFamily options are injected dynamically by the style panel via FontManagerService.
3978
- // Return empty array here; the panel overrides this in resolveOptions().
3979
- return [];
3980
- }
3981
- if (trait.type !== TraitInputType.Select) {
3982
- return undefined;
3983
- }
3984
- const opts = trait.options ?? [];
3985
- // Already formatted as {label, value}[]
3986
- if (opts.length > 0 && typeof opts[0] === 'object' && 'label' in opts[0]) {
3987
- return opts;
3988
- }
3989
- return opts.map((option) => ({ label: option, value: option }));
3990
- }
3991
- // ─── Style Groups ─────────────────────────────────────────────────────────────
3992
- const NON_STATIC_POSITIONS = ['relative', 'absolute', 'fixed', 'sticky'];
3993
- class PositionGroupStyleConfig extends GroupStyleConfig {
3994
- constructor() {
3995
- super(StyleCategory.Position, [
3996
- new TraitConfig('position', 'Position', TraitInputType.Select, ['static', 'relative', 'absolute', 'fixed', 'sticky'], 'static'),
3997
- new TraitConfig('top', 'T', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
3998
- new TraitConfig('right', 'R', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
3999
- new TraitConfig('bottom', 'B', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
4000
- new TraitConfig('left', 'L', TraitInputType.Scrub, { min: -9999, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true, { trait: 'position', values: NON_STATIC_POSITIONS }),
4001
- new TraitConfig('zIndex', 'Z', TraitInputType.Scrub, { min: -99, max: 9999, step: 1, units: [''] }, '0', false, { trait: 'position', values: NON_STATIC_POSITIONS }),
4002
- ]);
4003
- }
4004
- }
4005
- // ─── Layout ───────────────────────────────────────────────────────────────────
4006
- class LayoutGroupStyleConfig extends GroupStyleConfig {
4007
- constructor() {
4008
- super(StyleCategory.Layout, [
4009
- new TraitConfig('display', 'Display', TraitInputType.Select, ['block', 'inline', 'inline-block', 'flex', 'grid'], 'block'),
4010
- // ── Flex-only ─────────────────────────────────────────────────────
4011
- new TraitConfig('flexDirection', 'Direction', TraitInputType.SelectButton, [
4012
- { value: 'row', label: 'Horizontal', icon: 'ph-thin ph-arrow-right' },
4013
- { value: 'row-reverse', label: 'Horizontal Reversed', icon: 'ph-thin ph-arrow-left' },
4014
- { value: 'column', label: 'Vertical', icon: 'ph-thin ph-arrow-down' },
4015
- { value: 'column-reverse', label: 'Vertical Reversed', icon: 'ph-thin ph-arrow-up' },
4016
- ], 'row', false, { trait: 'display', values: 'flex' }),
4017
- new TraitConfig('flexWrap', 'Flex Wrap', TraitInputType.SelectButton, [
4018
- { value: 'nowrap', label: 'No Wrap', icon: 'ph-thin ph-minus' },
4019
- { value: 'wrap', label: 'Wrap', icon: 'ph-thin ph-arrow-elbow-down-right' },
4020
- { value: 'wrap-reverse', label: 'Wrap Reversed', icon: 'ph-thin ph-arrow-elbow-up-right' },
4021
- ], 'nowrap', false, { trait: 'display', values: 'flex' }),
4022
- // ── Shared flex + grid ────────────────────────────────────────────
4023
- new TraitConfig('justifyContent', 'Justify', TraitInputType.SelectButton, [
4024
- { value: 'flex-start', label: 'Start', icon: 'ph-thin ph-align-left' },
4025
- { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-horizontal' },
4026
- { value: 'flex-end', label: 'End', icon: 'ph-thin ph-align-right' },
4027
- { value: 'space-between', label: 'Space Between', icon: 'ph-thin ph-distribute-horizontal' },
4028
- { value: 'space-around', label: 'Space Around', icon: 'ph-thin ph-dots-three' },
4029
- { value: 'space-evenly', label: 'Space Evenly', icon: 'ph-thin ph-dots-six' },
4030
- ], 'flex-start', false, { trait: 'display', values: ['flex', 'grid'] }),
4031
- new TraitConfig('alignItems', 'Align Items', TraitInputType.SelectButton, [
4032
- { value: 'stretch', label: 'Stretch', icon: 'ph-thin ph-arrows-vertical' },
4033
- { value: 'start', label: 'Start', icon: 'ph-thin ph-align-top' },
4034
- { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-vertical' },
4035
- { value: 'end', label: 'End', icon: 'ph-thin ph-align-bottom' },
4036
- ], 'stretch', false, { trait: 'display', values: ['flex', 'grid'] }),
4037
- new TraitConfig('gap', 'Gap', TraitInputType.Scrub, { min: 0, max: 200, step: 1, units: UNITS_FIXED }, '0px', false, { trait: 'display', values: ['flex', 'grid'] }),
4038
- // ── Grid-only ─────────────────────────────────────────────────────
4039
- new TraitConfig('gridTemplateColumns', 'Columns', TraitInputType.GridTemplate, undefined, '1fr', false, { trait: 'display', values: 'grid' }),
4040
- new TraitConfig('gridTemplateRows', 'Rows', TraitInputType.GridTemplate, undefined, 'auto', false, { trait: 'display', values: 'grid' }),
4041
- new TraitConfig('justifyItems', 'Justify Items', TraitInputType.SelectButton, [
4042
- { value: 'stretch', label: 'Stretch', icon: 'ph-thin ph-arrows-horizontal' },
4043
- { value: 'start', label: 'Start', icon: 'ph-thin ph-align-left' },
4044
- { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-horizontal' },
4045
- { value: 'end', label: 'End', icon: 'ph-thin ph-align-right' },
4046
- ], 'stretch', false, { trait: 'display', values: 'grid' }),
4047
- new TraitConfig('alignContent', 'Align Content', TraitInputType.SelectButton, [
4048
- { value: 'start', label: 'Start', icon: 'ph-thin ph-align-top' },
4049
- { value: 'center', label: 'Center', icon: 'ph-thin ph-align-center-vertical' },
4050
- { value: 'end', label: 'End', icon: 'ph-thin ph-align-bottom' },
4051
- { value: 'space-between', label: 'Space Between', icon: 'ph-thin ph-distribute-vertical' },
4052
- { value: 'space-around', label: 'Space Around', icon: 'ph-thin ph-dots-three' },
4053
- { value: 'stretch', label: 'Stretch', icon: 'ph-thin ph-arrows-vertical' },
4054
- ], 'stretch', false, { trait: 'display', values: 'grid' }),
4055
- // ── Direction (always visible) ────────────────────────────────────
4056
- new TraitConfig('direction', 'Text Direction', TraitInputType.SelectButton, [
4057
- { value: 'ltr', label: 'LTR' },
4058
- { value: 'rtl', label: 'RTL' },
4059
- ], 'ltr'),
4060
- ]);
4061
- }
4062
- }
4063
- // ─── Size (replaces Dimensions) ───────────────────────────────────────────────
4064
- class SizeGroupStyleConfig extends GroupStyleConfig {
4065
- constructor() {
4066
- super(StyleCategory.Size, [
4067
- new TraitConfig('width', 'W', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
4068
- new TraitConfig('height', 'H', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
4069
- new TraitConfig('minWidth', 'Min W', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
4070
- new TraitConfig('minHeight', 'Min H', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
4071
- new TraitConfig('maxWidth', 'Max W', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
4072
- new TraitConfig('maxHeight', 'Max H', TraitInputType.Scrub, { min: 0, max: 9999, step: 1, units: UNITS_ALL, allowAuto: true }, 'auto', true),
4073
- new TraitConfig('aspectRatio', 'Aspect Ratio', TraitInputType.Text, undefined, 'auto'),
4074
- ]);
4075
- }
4076
- }
4077
- /** @deprecated Use SizeGroupStyleConfig instead. */
4078
- class DimensionsGroupStyleConfig extends SizeGroupStyleConfig {
4079
- }
4080
- // ─── Background ───────────────────────────────────────────────────────────────
4081
- class BackgroundGroupStyleConfig extends GroupStyleConfig {
4082
- constructor() {
4083
- super(StyleCategory.Background, [
4084
- new TraitConfig('backgroundColor', 'Background', TraitInputType.Color, { allowGradient: true, allowTransparent: true, formats: ['hex', 'rgb', 'hsl', 'transparent'] }, 'transparent'),
4085
- ]);
4086
- }
4087
- }
4088
- class SpacingGroupStyleConfig extends GroupStyleConfig {
4089
- constructor() {
4090
- super(StyleCategory.Spacing, [
4091
- new TraitConfig('margin', 'Margin', TraitInputType.DirectionalSize, {
4092
- allowNegative: true,
4093
- allowLinkedSlider: true,
4094
- slider: { min: 0, max: 200, step: 1 }
4095
- }, '0px 0px 0px 0px'),
4096
- new TraitConfig('padding', 'Padding', TraitInputType.DirectionalSize, {
4097
- allowNegative: false,
4098
- allowLinkedSlider: true,
4099
- slider: { min: 0, max: 200, step: 1 }
4100
- }, '0px 0px 0px 0px')
4101
- ]);
4102
- }
4103
- }
4104
- class BorderGroupStyleConfig extends GroupStyleConfig {
4105
- constructor() {
4106
- super(StyleCategory.Border, [
4107
- new TraitConfig('borderWidth', 'Border Width', TraitInputType.DirectionalSize, {
4108
- units: UNITS_FIXED,
4109
- allowNegative: false,
4110
- allowLinkedSlider: true,
4111
- slider: { min: 0, max: 32, step: 1 }
4112
- }, '0px'),
4113
- new TraitConfig('borderStyle', 'Border Style', TraitInputType.SelectButton, [
4114
- { value: 'solid', icon: 'ph-thin ph-line-segment' },
4115
- { value: 'dashed', icon: 'ph-thin ph-dots-three' },
4116
- { value: 'dotted', icon: 'ph-thin ph-dots-six' },
4117
- ], 'solid'),
4118
- new TraitConfig('borderColor', 'Border Color', TraitInputType.Color, { allowGradient: false, allowTransparent: false, formats: ['hex', 'rgb', 'hsl'] }, '#CCCCCC'),
4119
- new TraitConfig('borderRadius', 'Border Radius', TraitInputType.DirectionalSize, {
4120
- units: UNITS_FIXED,
4121
- allowNegative: false,
4122
- allowLinkedSlider: true,
4123
- linkByDefault: true,
4124
- slider: { min: 0, max: 120, step: 1 },
4125
- segments: [
4126
- { key: 'top-left', icon: 'ph-arrow-bend-up-left', ariaLabel: 'top left radius' },
4127
- { key: 'top-right', icon: 'ph-arrow-bend-up-right', ariaLabel: 'top right radius' },
4128
- { key: 'bottom-right', icon: 'ph-arrow-bend-down-right', ariaLabel: 'bottom right radius' },
4129
- { key: 'bottom-left', icon: 'ph-arrow-bend-down-left', ariaLabel: 'bottom left radius' }
4130
- ]
4131
- }, '0px')
4132
- ]);
4133
- }
4134
- }
4135
- class TypographyGroupStyleConfig extends GroupStyleConfig {
4136
- constructor() {
4137
- super(StyleCategory.Typography, [
4138
- new TraitConfig('fontFamily', 'Font Family', TraitInputType.FontFamily, [], 'Poppins, sans-serif'),
4139
- new TraitConfig('fontSize', 'Font Size', TraitInputType.Scrub, { min: 8, max: 200, step: 1, units: UNITS_FIXED }, '16px'),
4140
- new TraitConfig('fontWeight', 'Font Weight', TraitInputType.Select, ['100', '200', '300', '400', '500', '600', '700', '800', '900'], '400'),
4141
- new TraitConfig('color', 'Color', TraitInputType.Color, { allowGradient: false, allowTransparent: false, formats: ['hex', 'rgb', 'hsl'] }, '#000000'),
4142
- new TraitConfig('lineHeight', 'Line Height', TraitInputType.Select, ['1', '1.2', '1.4', '1.5', '1.75', '2', '2.5'], '1.5'),
4143
- new TraitConfig('letterSpacing', 'Letter Spacing', TraitInputType.Scrub, { min: -5, max: 50, step: 0.5, units: UNITS_FIXED }, '0px'),
4144
- new TraitConfig('textAlign', 'Alignment', TraitInputType.SelectButton, [
4145
- { value: 'left', icon: 'ph-thin ph-text-align-left' },
4146
- { value: 'center', icon: 'ph-thin ph-text-align-center' },
4147
- { value: 'right', icon: 'ph-thin ph-text-align-right' },
4148
- { value: 'justify', icon: 'ph-thin ph-text-align-justify' },
4149
- ], 'left'),
4150
- new TraitConfig('fontStyle', 'Italic', TraitInputType.SelectButton, { items: [{ value: 'italic', icon: 'ph-thin ph-text-italic', label: 'Italic' }], allowEmpty: true, noneValue: 'normal' }, 'normal', true),
4151
- new TraitConfig('textDecoration', 'Underline', TraitInputType.SelectButton, { items: [{ value: 'underline', icon: 'ph-thin ph-text-underline', label: 'Underline' }], allowEmpty: true, noneValue: 'none' }, 'none', true),
4152
- ]);
4153
- }
4154
- }
4155
- class EffectsGroupStyleConfig extends GroupStyleConfig {
4156
- constructor() {
4157
- super(StyleCategory.Effects, [
4158
- new TraitConfig('opacity', 'Opacity', TraitInputType.Scrub, { min: 0, max: 1, step: 0.01, units: [''] }, '1'),
4159
- new TraitConfig('mixBlendMode', 'Blend Mode', TraitInputType.Select, [
4160
- 'normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten',
4161
- 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
4162
- 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity',
4163
- ], 'normal'),
4164
- new TraitConfig('cursor', 'Cursor', TraitInputType.Select, [
4165
- 'auto', 'default', 'pointer', 'text', 'move', 'not-allowed',
4166
- 'grab', 'grabbing', 'crosshair', 'zoom-in', 'zoom-out', 'none',
4167
- ], 'auto'),
4168
- ]);
4169
- }
4170
- }
4171
- function flattenSchemaFields(properties, prefix = '') {
4172
- const result = [];
4173
- for (const [key, field] of Object.entries(properties)) {
4174
- const fullPath = prefix ? `${prefix}.${key}` : key;
4175
- if (field.type === 'object' && field.properties) {
4176
- result.push(...flattenSchemaFields(field.properties, fullPath));
4177
- }
4178
- else {
4179
- result.push({ label: fullPath, value: fullPath });
4180
- }
4181
- }
4182
- return result;
4183
- }
4184
-
4185
4192
  class InteractionsPanelComponent {
4186
4193
  constructor(overlayService) {
4187
4194
  this.overlayService = overlayService;
@@ -4872,5 +4879,5 @@ class TextBlockComponentConfig extends ComponentConfig {
4872
4879
  * Generated bundle index. Do not edit.
4873
4880
  */
4874
4881
 
4875
- export { ACTION_TYPE_OPTIONS, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CardComponentConfig, ComponentConfig, ComponentRegistryService, DEVICE_OPTIONS, DataSourceRegistryService, DeviceMode, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, GroupStyleConfig, IOX_CONTENT_SERVICE, IOX_FONT_MANAGER, InteractionEngineService, InteractionsPanelComponent, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LayerTreeComponent, NodeAction, OverlayComponent, OverlayService, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PresetRegistryService, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, ViewportService, ZOOM_OPTIONS, buildPresetStyleTraits, defaultPageSettings, generateNodeId, resolveTraitControllerType, resolveTraitOptions };
4882
+ export { ACTION_TYPE_OPTIONS, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CardComponentConfig, ComponentConfig, ComponentRegistryService, DEVICE_OPTIONS, DataSourceRegistryService, DeviceMode, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, GroupStyleConfig, INTERACTION_STATES, IOX_CONTENT_SERVICE, IOX_FONT_MANAGER, InteractionEngineService, InteractionsPanelComponent, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LayerTreeComponent, NodeAction, OverlayComponent, OverlayService, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PresetRegistryService, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, ViewportService, ZOOM_OPTIONS, buildPresetStyleTraits, defaultPageSettings, generateNodeId, resolveTraitControllerType, resolveTraitOptions };
4876
4883
  //# sourceMappingURL=vectoriox-iox-builder.mjs.map