@shohojdhara/atomix 0.3.14 → 0.3.15
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.
- package/CHANGELOG.md +20 -0
- package/build-tools/EXAMPLES.md +372 -0
- package/build-tools/README.md +242 -0
- package/build-tools/__tests__/error-handler.test.js +230 -0
- package/build-tools/__tests__/index.test.js +141 -0
- package/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/build-tools/__tests__/utils.test.js +161 -0
- package/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/build-tools/error-handler.js +308 -0
- package/build-tools/index.d.ts +43 -0
- package/build-tools/index.js +88 -0
- package/build-tools/package.json +67 -0
- package/build-tools/rollup-plugin.js +236 -0
- package/build-tools/types.d.ts +163 -0
- package/build-tools/utils.js +203 -0
- package/build-tools/vite-plugin.js +161 -0
- package/build-tools/webpack-loader.js +123 -0
- package/dist/atomix.css +203 -90
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +3 -3
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/EXAMPLES.md +372 -0
- package/dist/build-tools/README.md +242 -0
- package/dist/build-tools/__tests__/error-handler.test.js +230 -0
- package/dist/build-tools/__tests__/index.test.js +141 -0
- package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/dist/build-tools/__tests__/utils.test.js +161 -0
- package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/dist/build-tools/error-handler.js +308 -0
- package/dist/build-tools/index.d.ts +43 -0
- package/dist/build-tools/index.js +88 -0
- package/dist/build-tools/package.json +67 -0
- package/dist/build-tools/rollup-plugin.js +236 -0
- package/dist/build-tools/types.d.ts +163 -0
- package/dist/build-tools/utils.js +203 -0
- package/dist/build-tools/vite-plugin.js +161 -0
- package/dist/build-tools/webpack-loader.js +123 -0
- package/dist/charts.d.ts +1 -1
- package/dist/charts.js +86 -57
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +136 -112
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +2 -5
- package/dist/forms.js +140 -128
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +136 -112
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +9 -61
- package/dist/index.esm.js +237 -286
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +250 -299
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +23 -8
- package/scripts/atomix-cli.js +170 -73
- package/scripts/cli/__tests__/README.md +81 -0
- package/scripts/cli/__tests__/basic.test.js +115 -0
- package/scripts/cli/__tests__/component-generator.test.js +332 -0
- package/scripts/cli/__tests__/integration.test.js +327 -0
- package/scripts/cli/__tests__/test-setup.js +133 -0
- package/scripts/cli/__tests__/token-manager.test.js +251 -0
- package/scripts/cli/__tests__/utils.test.js +161 -0
- package/scripts/cli/component-generator.js +253 -299
- package/scripts/cli/dependency-checker.js +355 -0
- package/scripts/cli/interactive-init.js +46 -5
- package/scripts/cli/template-manager.js +0 -2
- package/scripts/cli/templates/common-templates.js +636 -0
- package/scripts/cli/templates/composable-templates.js +148 -126
- package/scripts/cli/templates/index.js +23 -16
- package/scripts/cli/templates/project-templates.js +151 -23
- package/scripts/cli/templates/react-templates.js +280 -210
- package/scripts/cli/templates/scss-templates.js +90 -91
- package/scripts/cli/templates/testing-templates.js +206 -27
- package/scripts/cli/templates/testing-utils.js +278 -0
- package/scripts/cli/templates/types-templates.js +70 -56
- package/scripts/cli/theme-bridge.js +8 -2
- package/scripts/cli/token-manager.js +318 -206
- package/scripts/cli/utils.js +0 -1
- package/src/components/Accordion/Accordion.stories.tsx +369 -870
- package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
- package/src/components/AtomixGlass/glass-utils.ts +2 -2
- package/src/components/AtomixGlass/shader-utils.ts +5 -0
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -35
- package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
- package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
- package/src/components/Avatar/Avatar.stories.tsx +213 -1
- package/src/components/Badge/Badge.stories.tsx +121 -362
- package/src/components/Block/Block.stories.tsx +21 -12
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
- package/src/components/Button/Button.stories.tsx +463 -1126
- package/src/components/Button/Button.test.tsx +107 -0
- package/src/components/Button/Button.tsx +46 -50
- package/src/components/Button/ButtonGroup.stories.tsx +373 -217
- package/src/components/Callout/Callout.stories.tsx +289 -634
- package/src/components/Card/Card.stories.tsx +248 -68
- package/src/components/Chart/Chart.stories.tsx +150 -8
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
- package/src/components/Countdown/Countdown.stories.tsx +115 -8
- package/src/components/DataTable/DataTable.stories.tsx +346 -146
- package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
- package/src/components/Dropdown/Dropdown.stories.tsx +153 -33
- package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
- package/src/components/Footer/Footer.stories.tsx +392 -328
- package/src/components/Form/Checkbox.stories.tsx +140 -6
- package/src/components/Form/Checkbox.test.tsx +63 -0
- package/src/components/Form/Checkbox.tsx +87 -51
- package/src/components/Form/Form.stories.tsx +119 -20
- package/src/components/Form/FormGroup.stories.tsx +127 -4
- package/src/components/Form/Radio.stories.tsx +140 -5
- package/src/components/Form/Select.stories.tsx +140 -8
- package/src/components/Form/Textarea.stories.tsx +149 -6
- package/src/components/Hero/Hero.stories.tsx +333 -32
- package/src/components/List/List.stories.tsx +141 -3
- package/src/components/Modal/Modal.stories.tsx +181 -42
- package/src/components/Popover/Popover.stories.tsx +448 -98
- package/src/components/Progress/Progress.stories.tsx +167 -5
- package/src/components/River/River.stories.tsx +1 -1
- package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
- package/src/components/Spinner/Spinner.stories.tsx +102 -8
- package/src/components/Steps/Steps.stories.tsx +172 -43
- package/src/components/Tabs/Tabs.stories.tsx +136 -10
- package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
- package/src/components/Todo/Todo.stories.tsx +198 -9
- package/src/components/Toggle/Toggle.stories.tsx +126 -39
- package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
- package/src/components/Upload/Upload.stories.tsx +113 -24
- package/src/lib/README.md +2 -2
- package/src/lib/__tests__/theme-tools.test.ts +193 -0
- package/src/lib/composables/index.ts +2 -2
- package/src/lib/composables/useAtomixGlass.ts +28 -56
- package/src/lib/composables/useChartExport.ts +2 -7
- package/src/lib/composables/useDataTable.ts +46 -29
- package/src/lib/constants/components.ts +9 -32
- package/src/lib/theme/devtools/CLI.ts +1 -1
- package/src/lib/types/components.ts +1 -1
- package/src/lib/utils/__tests__/csv.test.ts +45 -0
- package/src/lib/utils/csv.ts +17 -0
- package/src/lib/utils/dataTableExport.ts +1 -10
- package/src/styles/01-settings/_index.scss +2 -1
- package/src/styles/01-settings/_settings.accordion.scss +28 -7
- package/src/styles/01-settings/_settings.colors.scss +11 -11
- package/src/styles/01-settings/_settings.typography.scss +5 -5
- package/src/styles/02-tools/_tools.utility-api.scss +14 -0
- package/src/styles/06-components/_components.accordion.scss +56 -14
- package/src/styles/06-components/_components.checkbox.scss +23 -17
- package/src/styles/99-utilities/_index.scss +2 -0
- package/src/styles/99-utilities/_utilities.scss +3 -1
- package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
- package/themes/dark-complementary/README.md +98 -0
- package/themes/dark-complementary/index.scss +158 -0
- package/themes/default-light/README.md +81 -0
- package/themes/default-light/index.scss +154 -0
- package/themes/high-contrast/README.md +105 -0
- package/themes/high-contrast/index.scss +172 -0
- package/themes/test-theme/README.md +38 -0
- package/themes/test-theme/index.scss +47 -0
- package/scripts/cli/templates-original-backup.js +0 -1655
- package/scripts/cli/templates_backup.js +0 -684
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
- package/src/lib/composables/useButton.ts +0 -93
- package/src/lib/composables/useCheckbox.ts +0 -70
|
@@ -328,47 +328,64 @@ export function useDataTable({
|
|
|
328
328
|
setCurrentPage(1);
|
|
329
329
|
}, []);
|
|
330
330
|
|
|
331
|
+
// Pre-process column filters to avoid redundant lookups and transformations
|
|
332
|
+
const activeColumnFilters = useMemo(() => {
|
|
333
|
+
if (!columnFilters) return [];
|
|
334
|
+
|
|
335
|
+
return Object.entries(columnFilterValues)
|
|
336
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
337
|
+
.map(([columnKey, value]) => {
|
|
338
|
+
const column = columns.find(col => col.key === columnKey);
|
|
339
|
+
if (!column || !column.filterable) return null;
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
key: columnKey,
|
|
343
|
+
value,
|
|
344
|
+
lowercaseValue: typeof value === 'string' ? value.toLowerCase() : String(value).toLowerCase(),
|
|
345
|
+
column,
|
|
346
|
+
};
|
|
347
|
+
})
|
|
348
|
+
.filter((f): f is NonNullable<typeof f> => f !== null);
|
|
349
|
+
}, [columnFilters, columnFilterValues, columns]);
|
|
350
|
+
|
|
331
351
|
// Filter data based on search query and column filters
|
|
332
352
|
const filteredData = useMemo(() => {
|
|
333
|
-
|
|
353
|
+
if (!searchQuery && activeColumnFilters.length === 0) {
|
|
354
|
+
return data;
|
|
355
|
+
}
|
|
334
356
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
357
|
+
const lowercaseQuery = searchQuery ? searchQuery.toLowerCase() : '';
|
|
358
|
+
|
|
359
|
+
return data.filter(row => {
|
|
360
|
+
// Apply global search
|
|
361
|
+
if (searchQuery) {
|
|
362
|
+
const matchesGlobal = visibleColumns.some(column => {
|
|
340
363
|
const value = row[column.key];
|
|
341
364
|
if (value == null) return false;
|
|
342
365
|
return String(value).toLowerCase().includes(lowercaseQuery);
|
|
343
366
|
});
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
// Apply column-specific filters
|
|
348
|
-
if (columnFilters) {
|
|
349
|
-
result = result.filter(row => {
|
|
350
|
-
return Object.entries(columnFilterValues).every(([columnKey, filterValue]) => {
|
|
351
|
-
if (!filterValue) return true;
|
|
352
|
-
|
|
353
|
-
const column = columns.find(col => col.key === columnKey);
|
|
354
|
-
if (!column || !column.filterable) return true;
|
|
367
|
+
if (!matchesGlobal) return false;
|
|
368
|
+
}
|
|
355
369
|
|
|
356
|
-
|
|
357
|
-
|
|
370
|
+
// Apply column-specific filters
|
|
371
|
+
for (let i = 0; i < activeColumnFilters.length; i++) {
|
|
372
|
+
const { key, value, lowercaseValue, column } = activeColumnFilters[i];
|
|
373
|
+
const cellValue = row[key];
|
|
358
374
|
|
|
359
|
-
|
|
360
|
-
if (column.filterFunction) {
|
|
361
|
-
return column.filterFunction(cellValue, filterValue);
|
|
362
|
-
}
|
|
375
|
+
if (cellValue == null) return false;
|
|
363
376
|
|
|
377
|
+
// Use custom filter function if provided
|
|
378
|
+
if (column.filterFunction) {
|
|
379
|
+
if (!column.filterFunction(cellValue, value)) return false;
|
|
380
|
+
} else {
|
|
364
381
|
// Default text filter
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
382
|
+
if (!String(cellValue).toLowerCase().includes(lowercaseValue)) return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
369
385
|
|
|
370
|
-
|
|
371
|
-
|
|
386
|
+
return true;
|
|
387
|
+
});
|
|
388
|
+
}, [data, visibleColumns, searchQuery, activeColumnFilters]);
|
|
372
389
|
|
|
373
390
|
// Sort data
|
|
374
391
|
const sortedData = useMemo(() => {
|
|
@@ -1522,37 +1522,6 @@ export const BLOCK = {
|
|
|
1522
1522
|
},
|
|
1523
1523
|
};
|
|
1524
1524
|
|
|
1525
|
-
/**
|
|
1526
|
-
* GlassContainer-specific constants
|
|
1527
|
-
*/
|
|
1528
|
-
export const GLASS_CONTAINER = {
|
|
1529
|
-
CLASSES: {
|
|
1530
|
-
BASE: 'c-glass-container',
|
|
1531
|
-
GLASS: 'c-glass-container__glass',
|
|
1532
|
-
WARP: 'c-glass-container__warp',
|
|
1533
|
-
CONTENT: 'c-glass-container__content',
|
|
1534
|
-
OVERLAY: 'c-glass-container__overlay',
|
|
1535
|
-
OVERLAY_VISIBLE: 'c-glass-container__overlay--visible',
|
|
1536
|
-
OVERLAY_HIDDEN: 'c-glass-container__overlay--hidden',
|
|
1537
|
-
OVERLAY_BLEND: 'c-glass-container__overlay-blend',
|
|
1538
|
-
BORDER: 'c-glass-container__border',
|
|
1539
|
-
BORDER_OVERLAY: 'c-glass-container__border-overlay',
|
|
1540
|
-
HOVER_EFFECT: 'c-glass-container__hover-effect',
|
|
1541
|
-
ACTIVE_EFFECT: 'c-glass-container__active-effect',
|
|
1542
|
-
INTERACTION_EFFECT: 'c-glass-container__interaction-effect',
|
|
1543
|
-
ACTIVE: 'c-glass-container--active',
|
|
1544
|
-
CLICKABLE: 'c-glass-container--clickable',
|
|
1545
|
-
},
|
|
1546
|
-
DISPLACEMENT_MAPS: {
|
|
1547
|
-
STANDARD:
|
|
1548
|
-
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjcwIiBoZWlnaHQ9IjY5IiB2aWV3Qm94PSIwIDAgMjcwIDY5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxyYWRpYWxHcmFkaWVudCBpZD0iZ3JhZGllbnQiIGN4PSI1MCUiIGN5PSI1MCUiIHI9IjUwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzgwODA4MCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzgwODA4MCIvPjwvcmFkaWFsR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZGllbnQpIi8+PC9zdmc+',
|
|
1549
|
-
POLAR:
|
|
1550
|
-
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjcwIiBoZWlnaHQ9IjY5IiB2aWV3Qm94PSIwIDAgMjcwIDY5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxyYWRpYWxHcmFkaWVudCBpZD0icG9sYXIiIGN4PSI1MCUiIGN5PSI1MCUiIHI9IjUwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzQwNDA0MCIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjODA4MDgwIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNDA0MDQwIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNwb2xhcikiLz48L3N2Zz4=',
|
|
1551
|
-
PROMINENT:
|
|
1552
|
-
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjcwIiBoZWlnaHQ9IjY5IiB2aWV3Qm94PSIwIDAgMjcwIDY5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0icHJvbWluZW50IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjNDA0MDQwIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiNjMGMwYzAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM0MDQwNDAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3Byb21pbmVudCkiLz48L3N2Zz4=',
|
|
1553
|
-
},
|
|
1554
|
-
};
|
|
1555
|
-
|
|
1556
1525
|
/**
|
|
1557
1526
|
* Footer-specific constants
|
|
1558
1527
|
*/
|
|
@@ -1698,9 +1667,17 @@ export const ATOMIX_GLASS = {
|
|
|
1698
1667
|
MIN_BLUR: 0.1,
|
|
1699
1668
|
MOUSE_INFLUENCE_DIVISOR: 100,
|
|
1700
1669
|
EDGE_FADE_PIXELS: 2,
|
|
1701
|
-
|
|
1670
|
+
// Note: This default must match the SCSS variable --atomix-radius-md
|
|
1671
|
+
// @see src/styles/01-settings/_settings.global.scss
|
|
1672
|
+
DEFAULT_CORNER_RADIUS: 16,
|
|
1702
1673
|
MAX_SIZE: 4096, // Maximum width/height for glass size
|
|
1703
1674
|
|
|
1675
|
+
// Palette for internal calculations (matches design system base colors)
|
|
1676
|
+
PALETTE: {
|
|
1677
|
+
WHITE: '255, 255, 255',
|
|
1678
|
+
BLACK: '0, 0, 0',
|
|
1679
|
+
},
|
|
1680
|
+
|
|
1704
1681
|
// Gradient calculation constants
|
|
1705
1682
|
GRADIENT: {
|
|
1706
1683
|
BASE_ANGLE: 135, // Base angle for border gradients (degrees)
|
|
@@ -199,7 +199,7 @@ export interface AtomixGlassProps {
|
|
|
199
199
|
/**
|
|
200
200
|
* Shader variant for shader mode
|
|
201
201
|
*/
|
|
202
|
-
shaderVariant?: 'liquidGlass' | 'premiumGlass';
|
|
202
|
+
shaderVariant?: 'liquidGlass' | 'premiumGlass' | 'appleFluid' | 'liquidMetal' | 'plasma' | 'waves' | 'noise';
|
|
203
203
|
|
|
204
204
|
/**
|
|
205
205
|
* Accessibility props
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { sanitizeCSVCell } from '../csv';
|
|
3
|
+
|
|
4
|
+
describe('sanitizeCSVCell', () => {
|
|
5
|
+
it('should handle normal strings', () => {
|
|
6
|
+
expect(sanitizeCSVCell('Hello World')).toBe('Hello World');
|
|
7
|
+
expect(sanitizeCSVCell('123')).toBe('123');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should handle numbers', () => {
|
|
11
|
+
expect(sanitizeCSVCell(123)).toBe('123');
|
|
12
|
+
expect(sanitizeCSVCell(0)).toBe('0');
|
|
13
|
+
// Negative numbers are prefixed with ' to prevent potential formula injection
|
|
14
|
+
expect(sanitizeCSVCell(-1)).toBe("'-1");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle null and undefined', () => {
|
|
18
|
+
expect(sanitizeCSVCell(null)).toBe('');
|
|
19
|
+
expect(sanitizeCSVCell(undefined)).toBe('');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should replace newlines and tabs with spaces', () => {
|
|
23
|
+
expect(sanitizeCSVCell('Hello\nWorld')).toBe('Hello World');
|
|
24
|
+
expect(sanitizeCSVCell('Hello\rWorld')).toBe('Hello World');
|
|
25
|
+
expect(sanitizeCSVCell('Hello\tWorld')).toBe('Hello World');
|
|
26
|
+
expect(sanitizeCSVCell('Hello\r\nWorld')).toBe('Hello World');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should escape double quotes', () => {
|
|
30
|
+
expect(sanitizeCSVCell('Hello "World"')).toBe('Hello ""World""');
|
|
31
|
+
expect(sanitizeCSVCell('"')).toBe('""');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should prevent formula injection', () => {
|
|
35
|
+
expect(sanitizeCSVCell('=SUM(A1:B1)')).toBe("'=SUM(A1:B1)");
|
|
36
|
+
expect(sanitizeCSVCell('+SUM(A1:B1)')).toBe("'+SUM(A1:B1)");
|
|
37
|
+
expect(sanitizeCSVCell('-SUM(A1:B1)')).toBe("'-SUM(A1:B1)");
|
|
38
|
+
expect(sanitizeCSVCell('@SUM(A1:B1)')).toBe("'@SUM(A1:B1)");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle mixed cases', () => {
|
|
42
|
+
// Dangerous character + quotes + newlines
|
|
43
|
+
expect(sanitizeCSVCell('=cmd|\' /C calc\'!A0\n')).toBe("'=cmd|' /C calc'!A0 ");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSV Utility Functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sanitize cell content to prevent CSV injection
|
|
7
|
+
*
|
|
8
|
+
* Replaces newlines and tabs with spaces, escapes double quotes,
|
|
9
|
+
* and prefixes dangerous characters (=, +, -, @) with a single quote
|
|
10
|
+
* to prevent formula injection.
|
|
11
|
+
*/
|
|
12
|
+
export function sanitizeCSVCell(cell: any): string {
|
|
13
|
+
const sanitized = String(cell ?? '').replace(/[\r\n\t]/g, ' ').replace(/"/g, '""');
|
|
14
|
+
// Prevent formula injection by prefixing dangerous characters
|
|
15
|
+
const dangerous = /^[=+\-@]/;
|
|
16
|
+
return dangerous.test(sanitized) ? `'${sanitized}` : sanitized;
|
|
17
|
+
}
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { DataTableColumn, ExportFormat } from '../types/components';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Sanitize cell content to prevent CSV injection
|
|
5
|
-
*/
|
|
6
|
-
function sanitizeCSVCell(cell: any): string {
|
|
7
|
-
const sanitized = String(cell ?? '').replace(/[\r\n\t]/g, ' ').replace(/"/g, '""');
|
|
8
|
-
// Prevent formula injection by prefixing dangerous characters
|
|
9
|
-
const dangerous = /^[=+\-@]/;
|
|
10
|
-
return dangerous.test(sanitized) ? `'${sanitized}` : sanitized;
|
|
11
|
-
}
|
|
2
|
+
import { sanitizeCSVCell } from './csv';
|
|
12
3
|
|
|
13
4
|
/**
|
|
14
5
|
* Export data as CSV
|
|
@@ -3,30 +3,51 @@
|
|
|
3
3
|
@use './settings.config' as config;
|
|
4
4
|
@use './settings.spacing' as *;
|
|
5
5
|
@use './settings.colors' as *;
|
|
6
|
+
@use './settings.typography' as *;
|
|
6
7
|
|
|
8
|
+
// Accordion dimensions
|
|
7
9
|
$accordion-width: 100% !default;
|
|
8
|
-
$accordion-padding-x: map.get($spacing-sizes, 5) !default;
|
|
9
|
-
$accordion-padding-y: map.get($spacing-sizes, 4) !default;
|
|
10
|
+
$accordion-padding-x: map.get($spacing-sizes, 5) !default; //20px
|
|
11
|
+
$accordion-padding-y: map.get($spacing-sizes, 4) !default; //16px
|
|
10
12
|
|
|
13
|
+
// Accordion borders
|
|
11
14
|
$accordion-border-width: calc(var(--#{config.$prefix}border-width) * 2) !default;
|
|
12
|
-
$accordion-border-color:
|
|
15
|
+
$accordion-border-color: var(--#{config.$prefix}primary-border-subtle) !default;
|
|
13
16
|
$accordion-border-radius: radius.$border-radius-sm !default;
|
|
14
17
|
|
|
18
|
+
// Accordion body
|
|
15
19
|
$accordion-body-padding-x: $accordion-padding-x !default;
|
|
16
|
-
$accordion-body-padding-y: map.get($spacing-sizes, 2) !default;
|
|
20
|
+
$accordion-body-padding-y: map.get($spacing-sizes, 2) !default; //8px
|
|
17
21
|
$accordion-body-color: var(--#{config.$prefix}body-color) !default;
|
|
18
22
|
$accordion-body-bg: var(--#{config.$prefix}body-bg) !default;
|
|
19
23
|
|
|
24
|
+
// Accordion header
|
|
20
25
|
$accordion-header-padding-x: $accordion-padding-x !default;
|
|
21
26
|
$accordion-header-padding-y: $accordion-padding-y !default;
|
|
22
27
|
$accordion-header-color: var(--#{config.$prefix}body-color) !default;
|
|
23
28
|
$accordion-header-bg: var(--#{config.$prefix}body-bg) !default;
|
|
24
|
-
|
|
25
29
|
$accordion-header-bg-hover: var(--#{config.$prefix}body-bg) !default;
|
|
30
|
+
$accordion-header-font-size: $font-size-base !default;
|
|
31
|
+
$accordion-header-font-weight: $font-weight-normal !default;
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
// Accordion icon
|
|
34
|
+
$accordion-icon-size: map.get($spacing-sizes, 5) !default; //20px
|
|
28
35
|
$accordion-icon-color: var(--#{config.$prefix}body-color) !default;
|
|
29
|
-
|
|
30
36
|
$accordion-icon-transform: 180deg !default;
|
|
31
37
|
|
|
38
|
+
// Accordion states
|
|
32
39
|
$accordion-disable-color: var(--#{config.$prefix}tertiary-text-emphasis) !default;
|
|
40
|
+
$accordion-focus-border-color: var(--#{config.$prefix}focus-border-color) !default;
|
|
41
|
+
|
|
42
|
+
// Accordion transitions
|
|
43
|
+
$accordion-transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !default;
|
|
44
|
+
$accordion-panel-transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1) !default;
|
|
45
|
+
|
|
46
|
+
// Accordion spacing
|
|
47
|
+
$accordion-item-gap: 0px !default;
|
|
48
|
+
$accordion-header-inner-gap: map.get($spacing-sizes, 2) !default; //8px
|
|
49
|
+
|
|
50
|
+
// Enable features
|
|
51
|
+
$accordion-enable-glass: true !default;
|
|
52
|
+
$accordion-enable-smooth-scroll: true !default;
|
|
53
|
+
$accordion-enable-focus-ring: true !default;
|
|
@@ -11,17 +11,17 @@ $black: #000000 !default;
|
|
|
11
11
|
$color-contrast-dark: #000000 !default;
|
|
12
12
|
$color-contrast-light: #ffffff !default;
|
|
13
13
|
|
|
14
|
-
// Primitives Primary Colors
|
|
15
|
-
$primary-1: #
|
|
16
|
-
$primary-2: #
|
|
17
|
-
$primary-3: #
|
|
18
|
-
$primary-4: #
|
|
19
|
-
$primary-5: #
|
|
20
|
-
$primary-6: #
|
|
21
|
-
$primary-7: #
|
|
22
|
-
$primary-8: #
|
|
23
|
-
$primary-9: #
|
|
24
|
-
$primary-10: #
|
|
14
|
+
// Primitives Primary Colors (Purple Scale)
|
|
15
|
+
$primary-1: #f5f3ff !default;
|
|
16
|
+
$primary-2: #ede9fe !default;
|
|
17
|
+
$primary-3: #ddd6fe !default;
|
|
18
|
+
$primary-4: #c4b5fd !default;
|
|
19
|
+
$primary-5: #a78bfa !default;
|
|
20
|
+
$primary-6: #8b5cf6 !default; // Balanced primary - main brand color (WCAG AA compliant)
|
|
21
|
+
$primary-7: #7c3aed !default;
|
|
22
|
+
$primary-8: #6d28d9 !default;
|
|
23
|
+
$primary-9: #5b21b6 !default;
|
|
24
|
+
$primary-10: #4c1d95 !default;
|
|
25
25
|
|
|
26
26
|
// Primitives Red Colors
|
|
27
27
|
$red-1: #fef2f2 !default;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Font, line-height, and color for body text, headings, and more.
|
|
4
4
|
|
|
5
5
|
// Font stacks with appropriate fallbacks
|
|
6
|
-
$
|
|
6
|
+
$roboto: 'Roboto', sans-serif !default;
|
|
7
7
|
|
|
8
8
|
$helvetica-neue:
|
|
9
9
|
'Helvetica Neue',
|
|
@@ -26,11 +26,11 @@ $system-font-stack:
|
|
|
26
26
|
Arial,
|
|
27
27
|
sans-serif !default;
|
|
28
28
|
|
|
29
|
-
$font-family-base: $
|
|
30
|
-
$font-family-sans-serif: $
|
|
29
|
+
$font-family-base: $roboto !default;
|
|
30
|
+
$font-family-sans-serif: $roboto !default;
|
|
31
31
|
$font-family-monospace:
|
|
32
32
|
SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace !default;
|
|
33
|
-
$headings-font-family: $
|
|
33
|
+
$headings-font-family: $roboto !default;
|
|
34
34
|
|
|
35
35
|
$font-weight-light: 300 !default;
|
|
36
36
|
$font-weight-normal: 400 !default;
|
|
@@ -92,4 +92,4 @@ $h5-letter-spaceing: -0.5px !default;
|
|
|
92
92
|
$h6-letter-spaceing: -0.5px !default;
|
|
93
93
|
|
|
94
94
|
$paragraph-margin-bottom: 0 !default;
|
|
95
|
-
$body-text-align: null !default;
|
|
95
|
+
$body-text-align: null !default;
|
|
@@ -80,6 +80,13 @@ $utility-defaults: (
|
|
|
80
80
|
} @else {
|
|
81
81
|
#{$property-class}: $value if(map.get($utility, rtl), null, !important);
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
// Add local CSS variables if specified
|
|
85
|
+
@if map.get($utility, local-vars) {
|
|
86
|
+
@each $local-var-name, $local-var-value in map.get($utility, local-vars) {
|
|
87
|
+
--#{$local-var-name}: #{$local-var-value};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
83
90
|
}
|
|
84
91
|
} @else {
|
|
85
92
|
// Generate class with custom class name
|
|
@@ -104,6 +111,13 @@ $utility-defaults: (
|
|
|
104
111
|
} @else {
|
|
105
112
|
#{$base-class}: $value if(map.get($utility, rtl), null, !important);
|
|
106
113
|
}
|
|
114
|
+
|
|
115
|
+
// Add local CSS variables if specified
|
|
116
|
+
@if map.get($utility, local-vars) {
|
|
117
|
+
@each $local-var-name, $local-var-value in map.get($utility, local-vars) {
|
|
118
|
+
--#{$local-var-name}: #{$local-var-value};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
107
121
|
}
|
|
108
122
|
}
|
|
109
123
|
}
|
|
@@ -25,6 +25,12 @@
|
|
|
25
25
|
--#{config.$prefix}accordion-icon-color: #{accordion.$accordion-icon-color};
|
|
26
26
|
--#{config.$prefix}accordion-icon-transform: #{accordion.$accordion-icon-transform};
|
|
27
27
|
--#{config.$prefix}accordion-disable-color: #{accordion.$accordion-disable-color};
|
|
28
|
+
--#{config.$prefix}accordion-focus-border-color: #{accordion.$accordion-focus-border-color};
|
|
29
|
+
--#{config.$prefix}accordion-header-font-size: #{accordion.$accordion-header-font-size};
|
|
30
|
+
--#{config.$prefix}accordion-header-font-weight: #{accordion.$accordion-header-font-weight};
|
|
31
|
+
--#{config.$prefix}accordion-header-inner-gap: #{rem(accordion.$accordion-header-inner-gap)};
|
|
32
|
+
--#{config.$prefix}accordion-transition: #{accordion.$accordion-transition};
|
|
33
|
+
--#{config.$prefix}accordion-panel-transition: #{accordion.$accordion-panel-transition};
|
|
28
34
|
--panel-height: 0px;
|
|
29
35
|
|
|
30
36
|
width: 100%;
|
|
@@ -33,6 +39,7 @@
|
|
|
33
39
|
var(--#{config.$prefix}accordion-border-color);
|
|
34
40
|
border-radius: var(--#{config.$prefix}accordion-border-radius);
|
|
35
41
|
overflow: hidden;
|
|
42
|
+
transition: var(--#{config.$prefix}accordion-transition);
|
|
36
43
|
|
|
37
44
|
&__header {
|
|
38
45
|
display: flex;
|
|
@@ -45,6 +52,9 @@
|
|
|
45
52
|
border: none;
|
|
46
53
|
outline: none;
|
|
47
54
|
cursor: pointer;
|
|
55
|
+
font-size: var(--#{config.$prefix}accordion-header-font-size);
|
|
56
|
+
font-weight: var(--#{config.$prefix}accordion-header-font-weight);
|
|
57
|
+
gap: var(--#{config.$prefix}accordion-header-inner-gap);
|
|
48
58
|
@include basic-transition(background);
|
|
49
59
|
|
|
50
60
|
@include dynamic-background(var(--#{config.$prefix}accordion-header-bg));
|
|
@@ -52,22 +62,32 @@
|
|
|
52
62
|
&--icon-left {
|
|
53
63
|
flex-direction: row-reverse;
|
|
54
64
|
justify-content: flex-end;
|
|
55
|
-
gap: var(--#{config.$prefix}accordion-header-padding-x);
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
&:hover {
|
|
59
68
|
--#{config.$prefix}accordion-header-bg: var(--#{config.$prefix}accordion-header-bg-hover);
|
|
60
69
|
}
|
|
70
|
+
|
|
71
|
+
@if accordion.$accordion-enable-focus-ring {
|
|
72
|
+
&:focus-visible {
|
|
73
|
+
outline: 2px solid var(--#{config.$prefix}accordion-focus-border-color);
|
|
74
|
+
outline-offset: 2px;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
&__title {
|
|
64
|
-
|
|
80
|
+
flex: 1;
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
&__icon {
|
|
68
84
|
color: var(--#{config.$prefix}accordion-icon-color);
|
|
69
85
|
font-size: var(--#{config.$prefix}accordion-icon-size);
|
|
70
86
|
will-change: transform;
|
|
87
|
+
flex-shrink: 0;
|
|
88
|
+
display: inline-flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: center;
|
|
71
91
|
|
|
72
92
|
@include basic-transition();
|
|
73
93
|
}
|
|
@@ -77,6 +97,10 @@
|
|
|
77
97
|
overflow: hidden;
|
|
78
98
|
will-change: height;
|
|
79
99
|
|
|
100
|
+
@if accordion.$accordion-enable-smooth-scroll {
|
|
101
|
+
scroll-behavior: smooth;
|
|
102
|
+
}
|
|
103
|
+
|
|
80
104
|
@include basic-transition();
|
|
81
105
|
}
|
|
82
106
|
|
|
@@ -107,25 +131,43 @@
|
|
|
107
131
|
|
|
108
132
|
&.is-disabled {
|
|
109
133
|
pointer-events: none;
|
|
134
|
+
cursor: not-allowed;
|
|
110
135
|
|
|
111
136
|
--#{config.$prefix}accordion-header-color: var(--#{config.$prefix}accordion-disable-color);
|
|
112
137
|
--#{config.$prefix}accordion-body-color: var(--#{config.$prefix}accordion-disable-color);
|
|
113
138
|
--#{config.$prefix}accordion-icon-color: var(--#{config.$prefix}accordion-disable-color);
|
|
114
|
-
}
|
|
115
139
|
|
|
116
|
-
&--glass {
|
|
117
|
-
border-color: transparent;
|
|
118
140
|
#{$root}__header {
|
|
119
|
-
|
|
120
|
-
var(--#{config.$prefix}accordion-header-bg),
|
|
121
|
-
$background-transparency-enable: true
|
|
122
|
-
);
|
|
141
|
+
cursor: not-allowed;
|
|
123
142
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@if accordion.$accordion-enable-glass {
|
|
146
|
+
&--glass {
|
|
147
|
+
border-color: transparent;
|
|
148
|
+
#{$root}__header {
|
|
149
|
+
@include dynamic-background(
|
|
150
|
+
var(--#{config.$prefix}accordion-header-bg),
|
|
151
|
+
$background-transparency-enable: true
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
#{$root}__body {
|
|
155
|
+
@include dynamic-background(
|
|
156
|
+
var(--#{config.$prefix}accordion-header-bg),
|
|
157
|
+
$background-transparency-enable: true
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Reduced motion support
|
|
164
|
+
@media (prefers-reduced-motion: reduce) {
|
|
165
|
+
transition: none;
|
|
166
|
+
|
|
167
|
+
#{$root}__icon,
|
|
168
|
+
#{$root}__panel,
|
|
169
|
+
#{$root}__header {
|
|
170
|
+
transition: none;
|
|
129
171
|
}
|
|
130
172
|
}
|
|
131
173
|
}
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
--#{$prefix}checkbox-border-color-hover: #{$checkbox-border-color-hover};
|
|
27
27
|
--#{$prefix}checkbox-border-color-disabled: #{$checkbox-border-color-disabled};
|
|
28
28
|
--#{$prefix}checkbox-border-color-error: #{$checkbox-border-color-error};
|
|
29
|
+
--#{$prefix}checkbox-checked-border-color: #{$checkbox-checked-bg};
|
|
29
30
|
|
|
30
31
|
display: flex;
|
|
31
32
|
gap: var(--#{$prefix}checkbox-spacer);
|
|
@@ -48,16 +49,22 @@
|
|
|
48
49
|
|
|
49
50
|
&::before {
|
|
50
51
|
position: absolute;
|
|
51
|
-
top:
|
|
52
|
-
left:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
top: 0;
|
|
53
|
+
left: 0;
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
color: var(--#{$prefix}checkbox-checked-text-color);
|
|
60
|
+
background: var(--#{$prefix}checkbox-checked-bg);
|
|
61
|
+
content: '';
|
|
57
62
|
transform: scale(0);
|
|
58
|
-
|
|
59
|
-
transform-
|
|
60
|
-
|
|
63
|
+
opacity: 0;
|
|
64
|
+
transition: transform 120ms ease-in-out, opacity 120ms ease-in-out;
|
|
65
|
+
font-size: 0.65em;
|
|
66
|
+
line-height: 1;
|
|
67
|
+
font-family: 'Lux Icon';
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
&:hover {
|
|
@@ -65,23 +72,21 @@
|
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
&:checked {
|
|
68
|
-
border-color: var(--#{$prefix}checkbox-checked-
|
|
75
|
+
border-color: var(--#{$prefix}checkbox-checked-border-color);
|
|
69
76
|
|
|
70
77
|
&::before {
|
|
78
|
+
content: '\ef10';
|
|
71
79
|
transform: scale(1);
|
|
80
|
+
opacity: 1;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
&:hover {
|
|
75
84
|
border-color: var(--#{$prefix}checkbox-checked-bg-hover);
|
|
76
85
|
|
|
77
86
|
&::before {
|
|
78
|
-
|
|
87
|
+
background: var(--#{$prefix}checkbox-checked-bg-hover);
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
|
-
|
|
82
|
-
& + #{$root} {
|
|
83
|
-
color: red;
|
|
84
|
-
}
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
&:focus-visible {
|
|
@@ -93,7 +98,7 @@
|
|
|
93
98
|
cursor: not-allowed;
|
|
94
99
|
|
|
95
100
|
&::before {
|
|
96
|
-
|
|
101
|
+
background: var(--#{$prefix}checkbox-checked-bg-disabled) !important;
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
@@ -112,7 +117,7 @@
|
|
|
112
117
|
cursor: not-allowed;
|
|
113
118
|
|
|
114
119
|
&::before {
|
|
115
|
-
|
|
120
|
+
background: var(--#{$prefix}checkbox-checked-bg-disabled) !important;
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
}
|
|
@@ -130,6 +135,7 @@
|
|
|
130
135
|
#{$root} {
|
|
131
136
|
&__input::before {
|
|
132
137
|
content: '\f0d6';
|
|
138
|
+
background: var(--#{$prefix}checkbox-checked-bg);
|
|
133
139
|
}
|
|
134
140
|
}
|
|
135
141
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
@forward 'utilities.display';
|
|
6
6
|
@forward 'utilities.flex';
|
|
7
7
|
@forward 'utilities.glass-fixes';
|
|
8
|
+
@forward 'utilities.gradient';
|
|
8
9
|
@forward 'utilities.link';
|
|
9
10
|
@forward 'utilities.object-fit';
|
|
10
11
|
@forward 'utilities.opacity';
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
@forward 'utilities.sizes';
|
|
15
16
|
@forward 'utilities.spacing';
|
|
16
17
|
@forward 'utilities.text';
|
|
18
|
+
@forward 'utilities.text-gradient';
|
|
17
19
|
@forward 'utilities.visibility';
|
|
18
20
|
@forward 'utilities.visually-hidden';
|
|
19
21
|
@forward 'utilities.z-index';
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
@use 'utilities.sizes' as sizes;
|
|
20
20
|
@use 'utilities.spacing' as spacing;
|
|
21
21
|
@use 'utilities.text' as text;
|
|
22
|
+
@use 'utilities.text-gradient' as textGradient;
|
|
22
23
|
@use 'utilities.visibility' as visibility;
|
|
23
24
|
@use 'utilities.z-index' as z-index;
|
|
24
25
|
|
|
@@ -40,6 +41,7 @@ $utilities: map.merge($utilities, spacing.$utilities-spacing);
|
|
|
40
41
|
$utilities: map.merge($utilities, text.$utilities-text);
|
|
41
42
|
$utilities: map.merge($utilities, visibility.$utilities-visibility);
|
|
42
43
|
$utilities: map.merge($utilities, z-index.$utilities-z-index);
|
|
44
|
+
$utilities: map.merge($utilities, textGradient.$utilities-text-gradient);
|
|
43
45
|
|
|
44
46
|
// Generate all utility classes
|
|
45
|
-
@include util-api.generate-utilities($utilities, breakpoints.$grid-breakpoints);
|
|
47
|
+
@include util-api.generate-utilities($utilities, breakpoints.$grid-breakpoints);
|