@keenmate/pure-css 1.0.0-rc03 → 1.0.0-rc05
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 +195 -0
- package/README.md +69 -23
- package/dist/css/base.css +48 -204
- package/dist/css/grid.css +364 -364
- package/dist/css/pure-css.css +1726 -612
- package/dist/css/reboot.css +5 -5
- package/dist/css/scrollbars.css +9 -9
- package/dist/css/utilities.css +78 -30
- package/package.json +18 -3
- package/src/js/container-breakpoint.js +308 -0
- package/src/js/fit.js +890 -0
- package/src/js/navbar-dropdown.js +164 -0
- package/src/js/pure-css.js +305 -0
- package/src/js/sidebar-resize.js +210 -0
- package/src/scss/_base-css-variables.scss +360 -385
- package/src/scss/_fit-flyout.scss +76 -0
- package/src/scss/_fonts.scss +36 -36
- package/src/scss/_layout-container.scss +168 -0
- package/src/scss/_layout-responsive.scss +181 -0
- package/src/scss/_navbar-elements.scss +419 -0
- package/src/scss/_navbar.scss +120 -0
- package/src/scss/_pa-grid.scss +43 -43
- package/src/scss/_resize-handle.scss +76 -0
- package/src/scss/_sidebar-states.scss +243 -0
- package/src/scss/_sidebar.scss +420 -0
- package/src/scss/base.scss +2 -3
- package/src/scss/grid.scss +1 -1
- package/src/scss/pure-css.scss +20 -5
- package/src/scss/reboot.scss +5 -5
- package/src/scss/scrollbars.scss +10 -10
- package/src/scss/utilities.scss +791 -758
- package/src/scss/variables/_base.scss +232 -232
- package/src/scss/variables/_components.scss +840 -835
- package/src/scss/variables/_layout.scss +62 -62
- package/src/scss/variables/_system.scss +1 -1
- package/src/scss/variables/_typography.scss +37 -37
package/dist/css/reboot.css
CHANGED
|
@@ -17,16 +17,16 @@ p {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
:where(a) {
|
|
20
|
-
color: var(--
|
|
20
|
+
color: var(--pc-link-color);
|
|
21
21
|
text-decoration: underline;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
:where(a:hover) {
|
|
25
|
-
color: var(--
|
|
25
|
+
color: var(--pc-link-color-hover);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
:where(a:visited) {
|
|
29
|
-
color: var(--
|
|
29
|
+
color: var(--pc-link-color-visited);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
ul, ol {
|
|
@@ -79,8 +79,8 @@ body {
|
|
|
79
79
|
padding: 0;
|
|
80
80
|
font-family: var(--base-font-family);
|
|
81
81
|
font-size: 1.6rem;
|
|
82
|
-
color: var(--
|
|
83
|
-
background-color: var(--
|
|
82
|
+
color: var(--pc-text-color-1);
|
|
83
|
+
background-color: var(--pc-main-bg);
|
|
84
84
|
line-height: 1.5;
|
|
85
85
|
}
|
|
86
86
|
|
package/dist/css/scrollbars.css
CHANGED
|
@@ -5,28 +5,28 @@
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
*::-webkit-scrollbar-track {
|
|
8
|
-
background: var(--
|
|
9
|
-
border-radius: var(--
|
|
8
|
+
background: var(--pc-main-bg);
|
|
9
|
+
border-radius: var(--pc-border-radius-sm);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
*::-webkit-scrollbar-thumb {
|
|
13
|
-
background: var(--
|
|
14
|
-
border-radius: var(--
|
|
15
|
-
border: 1px solid var(--
|
|
13
|
+
background: var(--pc-border-color);
|
|
14
|
+
border-radius: var(--pc-border-radius-sm);
|
|
15
|
+
border: 1px solid var(--pc-main-bg);
|
|
16
16
|
}
|
|
17
17
|
*::-webkit-scrollbar-thumb:hover {
|
|
18
|
-
background: var(--
|
|
18
|
+
background: var(--pc-accent);
|
|
19
19
|
}
|
|
20
20
|
*::-webkit-scrollbar-thumb:active {
|
|
21
|
-
background: var(--
|
|
21
|
+
background: var(--pc-accent-hover);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
*::-webkit-scrollbar-corner {
|
|
25
|
-
background: var(--
|
|
25
|
+
background: var(--pc-main-bg);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/* Firefox */
|
|
29
29
|
* {
|
|
30
30
|
scrollbar-width: thin;
|
|
31
|
-
scrollbar-color: var(--
|
|
31
|
+
scrollbar-color: var(--pc-border-color) var(--pc-main-bg);
|
|
32
32
|
}
|
package/dist/css/utilities.css
CHANGED
|
@@ -1357,6 +1357,30 @@
|
|
|
1357
1357
|
flex-shrink: 1 !important;
|
|
1358
1358
|
}
|
|
1359
1359
|
|
|
1360
|
+
.flex-1 {
|
|
1361
|
+
flex: 1 1 0% !important;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.flex-auto {
|
|
1365
|
+
flex: 1 1 auto !important;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.flex-initial {
|
|
1369
|
+
flex: 0 1 auto !important;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.flex-none {
|
|
1373
|
+
flex: none !important;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
.flex-grow {
|
|
1377
|
+
flex-grow: 1 !important;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
.flex-shrink {
|
|
1381
|
+
flex-shrink: 1 !important;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1360
1384
|
.text-center {
|
|
1361
1385
|
text-align: center !important;
|
|
1362
1386
|
}
|
|
@@ -2052,6 +2076,30 @@
|
|
|
2052
2076
|
max-height: 50rem !important;
|
|
2053
2077
|
}
|
|
2054
2078
|
|
|
2079
|
+
.h-full {
|
|
2080
|
+
height: 100% !important;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
.h-screen {
|
|
2084
|
+
height: 100vh !important;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
.min-h-full {
|
|
2088
|
+
min-height: 100% !important;
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
.min-h-screen {
|
|
2092
|
+
min-height: 100vh !important;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
.max-h-full {
|
|
2096
|
+
max-height: 100% !important;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
.max-h-screen {
|
|
2100
|
+
max-height: 100vh !important;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2055
2103
|
.minw-5 {
|
|
2056
2104
|
min-width: 5% !important;
|
|
2057
2105
|
}
|
|
@@ -2461,23 +2509,23 @@
|
|
|
2461
2509
|
}
|
|
2462
2510
|
|
|
2463
2511
|
.border {
|
|
2464
|
-
border: 1px solid var(--
|
|
2512
|
+
border: 1px solid var(--pc-border-color) !important;
|
|
2465
2513
|
}
|
|
2466
2514
|
|
|
2467
2515
|
.border-top {
|
|
2468
|
-
border-top: 1px solid var(--
|
|
2516
|
+
border-top: 1px solid var(--pc-border-color) !important;
|
|
2469
2517
|
}
|
|
2470
2518
|
|
|
2471
2519
|
.border-right {
|
|
2472
|
-
border-right: 1px solid var(--
|
|
2520
|
+
border-right: 1px solid var(--pc-border-color) !important;
|
|
2473
2521
|
}
|
|
2474
2522
|
|
|
2475
2523
|
.border-bottom {
|
|
2476
|
-
border-bottom: 1px solid var(--
|
|
2524
|
+
border-bottom: 1px solid var(--pc-border-color) !important;
|
|
2477
2525
|
}
|
|
2478
2526
|
|
|
2479
2527
|
.border-left {
|
|
2480
|
-
border-left: 1px solid var(--
|
|
2528
|
+
border-left: 1px solid var(--pc-border-color) !important;
|
|
2481
2529
|
}
|
|
2482
2530
|
|
|
2483
2531
|
.border-0 {
|
|
@@ -2517,67 +2565,67 @@
|
|
|
2517
2565
|
}
|
|
2518
2566
|
|
|
2519
2567
|
.text-primary {
|
|
2520
|
-
color: var(--
|
|
2568
|
+
color: var(--pc-accent) !important;
|
|
2521
2569
|
}
|
|
2522
2570
|
|
|
2523
2571
|
.text-success {
|
|
2524
|
-
color: var(--
|
|
2572
|
+
color: var(--pc-success-text) !important;
|
|
2525
2573
|
}
|
|
2526
2574
|
|
|
2527
2575
|
.text-danger {
|
|
2528
|
-
color: var(--
|
|
2576
|
+
color: var(--pc-danger-text) !important;
|
|
2529
2577
|
}
|
|
2530
2578
|
|
|
2531
2579
|
.text-warning {
|
|
2532
|
-
color: var(--
|
|
2580
|
+
color: var(--pc-warning-text) !important;
|
|
2533
2581
|
}
|
|
2534
2582
|
|
|
2535
2583
|
.text-info {
|
|
2536
|
-
color: var(--
|
|
2584
|
+
color: var(--pc-info-text) !important;
|
|
2537
2585
|
}
|
|
2538
2586
|
|
|
2539
2587
|
.text-color-1 {
|
|
2540
|
-
color: var(--
|
|
2588
|
+
color: var(--pc-color-1) !important;
|
|
2541
2589
|
}
|
|
2542
2590
|
|
|
2543
2591
|
.text-color-2 {
|
|
2544
|
-
color: var(--
|
|
2592
|
+
color: var(--pc-color-2) !important;
|
|
2545
2593
|
}
|
|
2546
2594
|
|
|
2547
2595
|
.text-color-3 {
|
|
2548
|
-
color: var(--
|
|
2596
|
+
color: var(--pc-color-3) !important;
|
|
2549
2597
|
}
|
|
2550
2598
|
|
|
2551
2599
|
.text-color-4 {
|
|
2552
|
-
color: var(--
|
|
2600
|
+
color: var(--pc-color-4) !important;
|
|
2553
2601
|
}
|
|
2554
2602
|
|
|
2555
2603
|
.text-color-5 {
|
|
2556
|
-
color: var(--
|
|
2604
|
+
color: var(--pc-color-5) !important;
|
|
2557
2605
|
}
|
|
2558
2606
|
|
|
2559
2607
|
.text-color-6 {
|
|
2560
|
-
color: var(--
|
|
2608
|
+
color: var(--pc-color-6) !important;
|
|
2561
2609
|
}
|
|
2562
2610
|
|
|
2563
2611
|
.text-color-7 {
|
|
2564
|
-
color: var(--
|
|
2612
|
+
color: var(--pc-color-7) !important;
|
|
2565
2613
|
}
|
|
2566
2614
|
|
|
2567
2615
|
.text-color-8 {
|
|
2568
|
-
color: var(--
|
|
2616
|
+
color: var(--pc-color-8) !important;
|
|
2569
2617
|
}
|
|
2570
2618
|
|
|
2571
2619
|
.text-color-9 {
|
|
2572
|
-
color: var(--
|
|
2620
|
+
color: var(--pc-color-9) !important;
|
|
2573
2621
|
}
|
|
2574
2622
|
|
|
2575
2623
|
.rounded {
|
|
2576
|
-
border-radius: var(--
|
|
2624
|
+
border-radius: var(--pc-border-radius) !important;
|
|
2577
2625
|
}
|
|
2578
2626
|
|
|
2579
2627
|
.rounded-lg {
|
|
2580
|
-
border-radius: var(--
|
|
2628
|
+
border-radius: var(--pc-border-radius-lg) !important;
|
|
2581
2629
|
}
|
|
2582
2630
|
|
|
2583
2631
|
.rounded-circle {
|
|
@@ -2589,23 +2637,23 @@
|
|
|
2589
2637
|
}
|
|
2590
2638
|
|
|
2591
2639
|
.rounded-top {
|
|
2592
|
-
border-top-left-radius: var(--
|
|
2593
|
-
border-top-right-radius: var(--
|
|
2640
|
+
border-top-left-radius: var(--pc-border-radius) !important;
|
|
2641
|
+
border-top-right-radius: var(--pc-border-radius) !important;
|
|
2594
2642
|
}
|
|
2595
2643
|
|
|
2596
2644
|
.rounded-bottom {
|
|
2597
|
-
border-bottom-left-radius: var(--
|
|
2598
|
-
border-bottom-right-radius: var(--
|
|
2645
|
+
border-bottom-left-radius: var(--pc-border-radius) !important;
|
|
2646
|
+
border-bottom-right-radius: var(--pc-border-radius) !important;
|
|
2599
2647
|
}
|
|
2600
2648
|
|
|
2601
2649
|
.rounded-left {
|
|
2602
|
-
border-top-left-radius: var(--
|
|
2603
|
-
border-bottom-left-radius: var(--
|
|
2650
|
+
border-top-left-radius: var(--pc-border-radius) !important;
|
|
2651
|
+
border-bottom-left-radius: var(--pc-border-radius) !important;
|
|
2604
2652
|
}
|
|
2605
2653
|
|
|
2606
2654
|
.rounded-right {
|
|
2607
|
-
border-top-right-radius: var(--
|
|
2608
|
-
border-bottom-right-radius: var(--
|
|
2655
|
+
border-top-right-radius: var(--pc-border-radius) !important;
|
|
2656
|
+
border-bottom-right-radius: var(--pc-border-radius) !important;
|
|
2609
2657
|
}
|
|
2610
2658
|
|
|
2611
2659
|
.shadow-none {
|
|
@@ -2768,6 +2816,6 @@
|
|
|
2768
2816
|
row-gap: 5rem !important;
|
|
2769
2817
|
}
|
|
2770
2818
|
|
|
2771
|
-
.
|
|
2819
|
+
.pc-cq {
|
|
2772
2820
|
container-type: inline-size;
|
|
2773
2821
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/pure-css",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-rc05",
|
|
4
4
|
"description": "The KeenMate CSS foundation — --base-* theming contract, PureCSS grid and utility classes. Extracted from pure-admin-core so docs sites, standalone pages and every web/svelte component share one theming layer.",
|
|
5
5
|
"style": "dist/css/pure-css.css",
|
|
6
6
|
"exports": {
|
|
@@ -13,11 +13,14 @@
|
|
|
13
13
|
"./grid": "./dist/css/grid.css",
|
|
14
14
|
"./utilities": "./dist/css/utilities.css",
|
|
15
15
|
"./scss": "./src/scss/pure-css.scss",
|
|
16
|
-
"./scss/*": "./src/scss/*"
|
|
16
|
+
"./scss/*": "./src/scss/*",
|
|
17
|
+
"./js": "./src/js/pure-css.js",
|
|
18
|
+
"./js/*": "./src/js/*"
|
|
17
19
|
},
|
|
18
20
|
"files": [
|
|
19
21
|
"dist/",
|
|
20
22
|
"src/scss/",
|
|
23
|
+
"src/js/",
|
|
21
24
|
"README.md",
|
|
22
25
|
"CHANGELOG.md",
|
|
23
26
|
"LICENSE"
|
|
@@ -31,6 +34,12 @@
|
|
|
31
34
|
"build:utilities": "sass src/scss/utilities.scss dist/css/utilities.css --no-source-map --silence-deprecation=import",
|
|
32
35
|
"build": "npm run build:bundle && npm run build:base && npm run build:reboot && npm run build:scrollbars && npm run build:grid && npm run build:utilities",
|
|
33
36
|
"watch": "sass --watch src/scss/pure-css.scss:dist/css/pure-css.css --silence-deprecation=import",
|
|
37
|
+
"dev": "vite --port 12500",
|
|
38
|
+
"test": "npm run test:e2e",
|
|
39
|
+
"test:e2e": "playwright test",
|
|
40
|
+
"test:e2e:ui": "playwright test --ui",
|
|
41
|
+
"test:e2e:headed": "playwright test --headed",
|
|
42
|
+
"test:e2e:install": "playwright install chromium",
|
|
34
43
|
"prepublishOnly": "npm run build"
|
|
35
44
|
},
|
|
36
45
|
"keywords": [
|
|
@@ -50,7 +59,9 @@
|
|
|
50
59
|
"sass": "^1.0.0"
|
|
51
60
|
},
|
|
52
61
|
"devDependencies": {
|
|
53
|
-
"
|
|
62
|
+
"@playwright/test": "^1.60.0",
|
|
63
|
+
"sass": "^1.70.0",
|
|
64
|
+
"vite": "^5.4.0"
|
|
54
65
|
},
|
|
55
66
|
"repository": {
|
|
56
67
|
"type": "git",
|
|
@@ -62,5 +73,9 @@
|
|
|
62
73
|
"homepage": "https://github.com/KeenMate/pure-css#readme",
|
|
63
74
|
"publishConfig": {
|
|
64
75
|
"access": "public"
|
|
76
|
+
},
|
|
77
|
+
"allowScripts": {
|
|
78
|
+
"esbuild@0.21.5": true,
|
|
79
|
+
"@parcel/watcher@2.6.0": true
|
|
65
80
|
}
|
|
66
81
|
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure CSS — Container Breakpoint (element-width → named mode, event-driven)
|
|
3
|
+
*
|
|
4
|
+
* The JS counterpart to a CSS container query. A CSS `@container` rule can only
|
|
5
|
+
* SHOW / HIDE — it can never skip work. This watches an element's inline size
|
|
6
|
+
* with a ResizeObserver, maps it to a NAMED mode from author-declared width
|
|
7
|
+
* thresholds, and EMITS a change only when the mode actually flips. A framework
|
|
8
|
+
* wrapper turns that event into conditional rendering — so the branches for
|
|
9
|
+
* other modes are never MOUNTED (no off-screen Chart.js instance, no eager data
|
|
10
|
+
* fetch, no shadow-DOM widget built to be immediately hidden).
|
|
11
|
+
*
|
|
12
|
+
* This is the "expensive 2-D swap" tool; `fit.js` remains the "cheap 1-D row
|
|
13
|
+
* fold" tool. The difference is measurement:
|
|
14
|
+
* - fit.js is CONTENT-measured (it renders every variant to read its width),
|
|
15
|
+
* so it can't avoid mounting the losers.
|
|
16
|
+
* - container-breakpoint is THRESHOLD-declared (you name the widths), so the
|
|
17
|
+
* losers never need to exist — which is exactly what lets a wrapper mount
|
|
18
|
+
* on demand.
|
|
19
|
+
*
|
|
20
|
+
* Because it is threshold-declared it does NOT need `container-type` on the
|
|
21
|
+
* element (the ResizeObserver measures the box directly). It reads the element's
|
|
22
|
+
* CONTENT-box inline size, to match `@container inline-size` semantics — so a
|
|
23
|
+
* `data-mode` written here and an `@container` rule agree on width.
|
|
24
|
+
*
|
|
25
|
+
* ── Programmatic API (what a Svelte / vanilla caller uses) ──────────────────
|
|
26
|
+
* var handle = pureCss.components.containerBreakpoint.observe(el, {
|
|
27
|
+
* steps: { compact: 0, comfy: 34, wide: 64 }, // name → MIN inline size
|
|
28
|
+
* unit: 'rem', // 'rem' (default, resolved vs :root font-size) | 'px'
|
|
29
|
+
* hysteresis: 1, // dead-band (in `unit`) to stop boundary flapping
|
|
30
|
+
* attribute: 'data-mode', // reflect the mode here (false to not write any)
|
|
31
|
+
* initial: 'comfy', // mode written synchronously before first measure
|
|
32
|
+
* // (SSR/first-paint default; omit to skip)
|
|
33
|
+
* emitEvent: true // also dispatch a 'pc:breakpoint' CustomEvent on el
|
|
34
|
+
* }, function (mode, prev, detail) { ... }); // cb fires once on settle, then on change
|
|
35
|
+
*
|
|
36
|
+
* handle.current(); // → the current mode string
|
|
37
|
+
* handle.remeasure(); // force a re-evaluate (e.g. after a font swap)
|
|
38
|
+
* handle.destroy(); // disconnect the observer + drop the attribute
|
|
39
|
+
*
|
|
40
|
+
* The callback and the 'pc:breakpoint' event (detail: {mode, prev, width, el})
|
|
41
|
+
* are two faces of the same signal — use the callback from JS, the event from a
|
|
42
|
+
* framework hook (Phoenix LiveView, etc.) that can't take a closure.
|
|
43
|
+
*
|
|
44
|
+
* ── Declarative API (zero-framework / keen bridge) ──────────────────────────
|
|
45
|
+
* Wire it from markup — no JS call site — so CSS can key off [data-mode] and a
|
|
46
|
+
* LiveView hook can listen for 'pc:breakpoint':
|
|
47
|
+
*
|
|
48
|
+
* <div data-pc-breakpoints='{"compact":0,"comfy":34,"wide":64}'
|
|
49
|
+
* data-pc-breakpoint-initial="comfy">
|
|
50
|
+
* <div data-pc-show="wide">…rich…</div> <!-- .d-none unless mode = wide -->
|
|
51
|
+
* <div data-pc-show="compact comfy">…lite…</div>
|
|
52
|
+
* </div>
|
|
53
|
+
*
|
|
54
|
+
* pureCss.components.containerBreakpoint.initAll(); // or .init(el)
|
|
55
|
+
*
|
|
56
|
+
* data-pc-show="a b …" — a descendant appears ONLY in the listed modes; in any
|
|
57
|
+
* other mode the engine toggles the shared `.d-none` utility onto it (a CLASS,
|
|
58
|
+
* not inline display — so it reverts to its natural stylesheet display when
|
|
59
|
+
* shown, and you can watch the class hop in devtools, same idea as fit.js's
|
|
60
|
+
* `pc-fit-hidden`). Override the class per-call with opts.hiddenClass or globally
|
|
61
|
+
* via pureCss.config.containerBreakpoint.hiddenClass. Elements without
|
|
62
|
+
* data-pc-show are never touched — pure CSS `[data-mode]` styling still works
|
|
63
|
+
* alongside it.
|
|
64
|
+
*
|
|
65
|
+
* Optional attrs: data-pc-breakpoint-unit ("rem"|"px"),
|
|
66
|
+
* data-pc-breakpoint-hysteresis (number), data-pc-breakpoint-attr (attr name to
|
|
67
|
+
* reflect into, default "data-mode").
|
|
68
|
+
*
|
|
69
|
+
* Public API (window.pureCss.components.containerBreakpoint):
|
|
70
|
+
* observe(el, opts, cb) — wire one element; returns a handle (idempotent per call)
|
|
71
|
+
* init(el) — declarative wire from data-pc-breakpoints (idempotent)
|
|
72
|
+
* initAll(scope) — declarative wire every [data-pc-breakpoints] under scope
|
|
73
|
+
* relayoutAll() — force a re-measure of every declaratively-wired element
|
|
74
|
+
*/
|
|
75
|
+
(function () {
|
|
76
|
+
'use strict';
|
|
77
|
+
|
|
78
|
+
var SELECTOR = '[data-pc-breakpoints]';
|
|
79
|
+
|
|
80
|
+
// :root font-size in px, for resolving rem thresholds against the live base
|
|
81
|
+
// (pure-admin ships 10px, but resolve rather than assume so a consumer that
|
|
82
|
+
// changes it stays correct). Falls back to 16 if unreadable.
|
|
83
|
+
function rootFontPx() {
|
|
84
|
+
try {
|
|
85
|
+
var n = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
86
|
+
return (!isNaN(n) && n > 0) ? n : 16;
|
|
87
|
+
} catch (e) { return 16; }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Normalise `steps` (object {name:min} or array [{name,min}]) into an array of
|
|
91
|
+
// { name, min } sorted ASCENDING by min. min is in the caller's unit.
|
|
92
|
+
function normSteps(steps) {
|
|
93
|
+
var out = [];
|
|
94
|
+
if (Array.isArray(steps)) {
|
|
95
|
+
steps.forEach(function (s) {
|
|
96
|
+
if (s && s.name != null && isFinite(s.min)) out.push({ name: String(s.name), min: +s.min });
|
|
97
|
+
});
|
|
98
|
+
} else if (steps && typeof steps === 'object') {
|
|
99
|
+
Object.keys(steps).forEach(function (name) {
|
|
100
|
+
var v = parseFloat(steps[name]);
|
|
101
|
+
if (!isNaN(v)) out.push({ name: name, min: v });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
out.sort(function (a, b) { return a.min - b.min; });
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Largest step whose min <= width (px). Returns its index; 0 (the floor) when
|
|
109
|
+
// the element is narrower than every threshold.
|
|
110
|
+
function candidateIndex(widthPx, stepsPx) {
|
|
111
|
+
var idx = 0;
|
|
112
|
+
for (var i = 0; i < stepsPx.length; i++) {
|
|
113
|
+
if (widthPx >= stepsPx[i].min) idx = i; else break;
|
|
114
|
+
}
|
|
115
|
+
return idx;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Resolve the settled mode index with a dead-band so a width parked on a
|
|
119
|
+
// boundary doesn't flip every frame:
|
|
120
|
+
// - moving UP: accept the higher step only once width clears its min by band
|
|
121
|
+
// - moving DOWN: accept a lower step only once width drops below the CURRENT
|
|
122
|
+
// step's min by band
|
|
123
|
+
// First evaluation (curIdx == null) takes the plain candidate — no band, so the
|
|
124
|
+
// initial mode is a pure function of width.
|
|
125
|
+
function settleIndex(widthPx, stepsPx, curIdx, bandPx) {
|
|
126
|
+
var cand = candidateIndex(widthPx, stepsPx);
|
|
127
|
+
if (curIdx == null || cand === curIdx) return cand;
|
|
128
|
+
if (cand > curIdx) {
|
|
129
|
+
return (widthPx >= stepsPx[cand].min + bandPx) ? cand : curIdx;
|
|
130
|
+
}
|
|
131
|
+
// cand < curIdx
|
|
132
|
+
return (widthPx < stepsPx[curIdx].min - bandPx) ? cand : curIdx;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function observe(el, opts, cb) {
|
|
136
|
+
if (!el || el.nodeType !== 1) return null;
|
|
137
|
+
opts = opts || {};
|
|
138
|
+
|
|
139
|
+
var steps = normSteps(opts.steps);
|
|
140
|
+
if (!steps.length) return null; // nothing to switch between
|
|
141
|
+
|
|
142
|
+
var unit = opts.unit === 'px' ? 'px' : 'rem';
|
|
143
|
+
var hysteresis = isFinite(opts.hysteresis) ? +opts.hysteresis
|
|
144
|
+
: ((window.pureCss && window.pureCss.config && window.pureCss.config.containerBreakpoint
|
|
145
|
+
&& window.pureCss.config.containerBreakpoint.hysteresis) || 0);
|
|
146
|
+
var attribute = opts.attribute === false ? null : (opts.attribute || 'data-mode');
|
|
147
|
+
var emitEvent = opts.emitEvent !== false;
|
|
148
|
+
var hiddenClass = opts.hiddenClass
|
|
149
|
+
|| (window.pureCss && window.pureCss.config && window.pureCss.config.containerBreakpoint
|
|
150
|
+
&& window.pureCss.config.containerBreakpoint.hiddenClass)
|
|
151
|
+
|| 'd-none';
|
|
152
|
+
|
|
153
|
+
var curIdx = null; // settled step index; null until first measure
|
|
154
|
+
var curMode = null;
|
|
155
|
+
|
|
156
|
+
// Convert the declared thresholds + band into px for comparison. Recomputed
|
|
157
|
+
// per measure so a runtime root-font-size change is honoured (rem unit only).
|
|
158
|
+
function toPx() {
|
|
159
|
+
var base = unit === 'rem' ? rootFontPx() : 1;
|
|
160
|
+
return {
|
|
161
|
+
steps: steps.map(function (s) { return { name: s.name, min: s.min * base }; }),
|
|
162
|
+
band: hysteresis * base
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Toggle the shared hidden class on [data-pc-show] descendants: an element
|
|
167
|
+
// stays visible only in the modes listed in its data-pc-show; in any other
|
|
168
|
+
// mode it gets `hiddenClass`. A class (not inline display) so it reverts to
|
|
169
|
+
// its natural display when shown — and is visible "jumping" in devtools.
|
|
170
|
+
function applyVisibility(mode) {
|
|
171
|
+
if (!hiddenClass) return;
|
|
172
|
+
var shown = el.querySelectorAll('[data-pc-show]');
|
|
173
|
+
for (var i = 0; i < shown.length; i++) {
|
|
174
|
+
var tokens = (shown[i].getAttribute('data-pc-show') || '').split(/\s+/);
|
|
175
|
+
shown[i].classList.toggle(hiddenClass, tokens.indexOf(mode) === -1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function reflect(mode) {
|
|
180
|
+
if (attribute) el.setAttribute(attribute, mode);
|
|
181
|
+
applyVisibility(mode);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Optional synchronous default before the observer's first callback — lets a
|
|
185
|
+
// wrapper render a sane branch on first paint / SSR hydration.
|
|
186
|
+
if (opts.initial != null) {
|
|
187
|
+
curMode = String(opts.initial);
|
|
188
|
+
curIdx = -1; // sentinel: "have a provisional mode, but width not yet read"
|
|
189
|
+
reflect(curMode);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function evaluate(widthPx) {
|
|
193
|
+
var p = toPx();
|
|
194
|
+
// Treat the provisional `initial` as "no settled index yet" for the band.
|
|
195
|
+
var fromIdx = (curIdx == null || curIdx < 0) ? null : curIdx;
|
|
196
|
+
var nextIdx = settleIndex(widthPx, p.steps, fromIdx, p.band);
|
|
197
|
+
var nextMode = p.steps[nextIdx].name;
|
|
198
|
+
if (nextMode === curMode && curIdx === nextIdx) return;
|
|
199
|
+
var prev = curMode;
|
|
200
|
+
curIdx = nextIdx;
|
|
201
|
+
curMode = nextMode;
|
|
202
|
+
reflect(curMode);
|
|
203
|
+
var detail = { mode: curMode, prev: prev, width: widthPx, el: el };
|
|
204
|
+
if (typeof cb === 'function') {
|
|
205
|
+
try { cb(curMode, prev, detail); }
|
|
206
|
+
catch (e) { if (window.pureCss && window.pureCss.debug) window.pureCss.debug.log('container-breakpoint', 'callback threw', e); }
|
|
207
|
+
}
|
|
208
|
+
if (emitEvent && typeof CustomEvent === 'function') {
|
|
209
|
+
el.dispatchEvent(new CustomEvent('pc:breakpoint', { detail: detail, bubbles: false }));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Content-box inline size, matching @container inline-size. Prefer the
|
|
214
|
+
// observer entry's contentBoxSize; fall back to clientWidth minus padding.
|
|
215
|
+
function widthFromEntry(entry) {
|
|
216
|
+
if (entry && entry.contentBoxSize) {
|
|
217
|
+
var box = Array.isArray(entry.contentBoxSize) ? entry.contentBoxSize[0] : entry.contentBoxSize;
|
|
218
|
+
if (box && isFinite(box.inlineSize)) return box.inlineSize;
|
|
219
|
+
}
|
|
220
|
+
if (entry && entry.contentRect) return entry.contentRect.width;
|
|
221
|
+
var cs = getComputedStyle(el);
|
|
222
|
+
return el.clientWidth - (parseFloat(cs.paddingLeft) || 0) - (parseFloat(cs.paddingRight) || 0);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
var ro = null;
|
|
226
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
227
|
+
ro = new ResizeObserver(function (entries) {
|
|
228
|
+
for (var i = 0; i < entries.length; i++) {
|
|
229
|
+
if (entries[i].target === el) { evaluate(widthFromEntry(entries[i])); return; }
|
|
230
|
+
}
|
|
231
|
+
evaluate(widthFromEntry(null));
|
|
232
|
+
});
|
|
233
|
+
ro.observe(el);
|
|
234
|
+
} else {
|
|
235
|
+
// No ResizeObserver: settle once now, and follow the shared viewport source.
|
|
236
|
+
evaluate(widthFromEntry(null));
|
|
237
|
+
if (window.pureCss && window.pureCss.events) {
|
|
238
|
+
var off = window.pureCss.events.on('viewport:resize', function () { evaluate(widthFromEntry(null)); });
|
|
239
|
+
ro = { disconnect: off };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
current: function () { return curMode; },
|
|
245
|
+
remeasure: function () { evaluate(widthFromEntry(null)); },
|
|
246
|
+
destroy: function () {
|
|
247
|
+
if (ro && ro.disconnect) ro.disconnect();
|
|
248
|
+
if (attribute) el.removeAttribute(attribute);
|
|
249
|
+
if (hiddenClass) {
|
|
250
|
+
var shown = el.querySelectorAll('[data-pc-show]');
|
|
251
|
+
for (var i = 0; i < shown.length; i++) shown[i].classList.remove(hiddenClass);
|
|
252
|
+
}
|
|
253
|
+
el.__paCbHandle = null;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Declarative: read config off data-* attributes and observe.
|
|
259
|
+
function init(el) {
|
|
260
|
+
if (!el || el.__paCbHandle) return el && el.__paCbHandle;
|
|
261
|
+
var raw = el.getAttribute('data-pc-breakpoints');
|
|
262
|
+
if (!raw) return null;
|
|
263
|
+
var steps;
|
|
264
|
+
try { steps = JSON.parse(raw); }
|
|
265
|
+
catch (e) {
|
|
266
|
+
if (window.pureCss && window.pureCss.debug) window.pureCss.debug.log('container-breakpoint', 'bad data-pc-breakpoints JSON', raw, e);
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
var opts = {
|
|
270
|
+
steps: steps,
|
|
271
|
+
unit: el.getAttribute('data-pc-breakpoint-unit') || 'rem',
|
|
272
|
+
attribute: el.getAttribute('data-pc-breakpoint-attr') || 'data-mode',
|
|
273
|
+
initial: el.getAttribute('data-pc-breakpoint-initial') || undefined
|
|
274
|
+
};
|
|
275
|
+
var hy = el.getAttribute('data-pc-breakpoint-hysteresis');
|
|
276
|
+
if (hy != null && hy !== '') opts.hysteresis = parseFloat(hy);
|
|
277
|
+
var handle = observe(el, opts);
|
|
278
|
+
if (handle) el.__paCbHandle = handle;
|
|
279
|
+
return handle;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function initAll(scope) {
|
|
283
|
+
var root = scope || document;
|
|
284
|
+
var els = root.querySelectorAll(SELECTOR);
|
|
285
|
+
for (var i = 0; i < els.length; i++) init(els[i]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function relayoutAll() {
|
|
289
|
+
var els = document.querySelectorAll(SELECTOR);
|
|
290
|
+
for (var i = 0; i < els.length; i++) {
|
|
291
|
+
if (els[i].__paCbHandle) els[i].__paCbHandle.remeasure();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
var pa = (window.pureCss = window.pureCss || {});
|
|
296
|
+
(pa.components = pa.components || {}).containerBreakpoint = {
|
|
297
|
+
observe: observe,
|
|
298
|
+
init: init,
|
|
299
|
+
initAll: initAll,
|
|
300
|
+
relayoutAll: relayoutAll
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
if (document.readyState === 'loading') {
|
|
304
|
+
document.addEventListener('DOMContentLoaded', function () { initAll(); });
|
|
305
|
+
} else {
|
|
306
|
+
initAll();
|
|
307
|
+
}
|
|
308
|
+
})();
|