vanilla-framework 4.34.1 → 4.35.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-framework",
3
- "version": "4.34.1",
3
+ "version": "4.35.0",
4
4
  "author": {
5
5
  "email": "webteam@canonical.com",
6
6
  "name": "Canonical Webteam"
@@ -22,6 +22,17 @@
22
22
  "module"
23
23
  ],
24
24
  "license": "LGPL-3.0",
25
+ "module": "templates/static/js/index.js",
26
+ "exports": {
27
+ ".": {
28
+ "sass": "./_index.scss"
29
+ },
30
+ "./scss/*": {
31
+ "sass": "./scss/*"
32
+ },
33
+ "./js": "./templates/static/js/index.js",
34
+ "./js/*": "./templates/static/js/*.js"
35
+ },
25
36
  "repository": {
26
37
  "type": "git",
27
38
  "url": "https://github.com/canonical/vanilla-framework"
@@ -54,7 +65,8 @@
54
65
  "/scss",
55
66
  "!/scss/docs",
56
67
  "!/scss/standalone",
57
- "/templates/_macros"
68
+ "/templates/_macros",
69
+ "/templates/static/js"
58
70
  ],
59
71
  "devDependencies": {
60
72
  "@canonical/cookie-policy": "3.6.5",
@@ -52,7 +52,7 @@ $application-layout--side-nav-width-expanded: 15rem !default;
52
52
  'nav status status';
53
53
  grid-template-columns: min-content minmax(0, 1fr) minmax(0, min-content);
54
54
  grid-template-rows: min-content 1fr min-content;
55
- height: 100vh;
55
+ height: 100dvh;
56
56
  overflow: hidden; // make sure panels transformed off-screen don't make the layout scroll
57
57
  width: 100vw;
58
58
  }
@@ -69,7 +69,7 @@ $application-layout--side-nav-width-expanded: 15rem !default;
69
69
 
70
70
  bottom: 0;
71
71
  box-shadow: $panel-drop-shadow;
72
- height: 100vh;
72
+ height: 100dvh;
73
73
  left: 0;
74
74
  overflow-y: auto;
75
75
  position: fixed;
@@ -94,7 +94,7 @@ $application-layout--side-nav-width-expanded: 15rem !default;
94
94
  }
95
95
 
96
96
  .l-navigation__drawer {
97
- height: 100vh;
97
+ height: 100dvh;
98
98
  width: auto;
99
99
 
100
100
  @media (min-width: $breakpoint-x-small) {
@@ -90,7 +90,7 @@
90
90
 
91
91
  // on largest screens we want to keep the table of contents sticky
92
92
  .l-docs__sticky-container {
93
- max-height: 100vh;
93
+ max-height: 100dvh;
94
94
  overflow-y: auto;
95
95
  position: sticky;
96
96
  top: 0;
@@ -25,7 +25,7 @@
25
25
  $navigation-top-height: $spv--large * 2 + map-get($settings-text-default, line-height);
26
26
 
27
27
  height: calc(100% - $navigation-top-height); // height of document reduced by height of top nav
28
- min-height: calc(100vh - $navigation-top-height);
28
+ min-height: calc(100dvh - $navigation-top-height);
29
29
  position: absolute;
30
30
  width: $l-full-screen-aside-width;
31
31
  z-index: 1;
@@ -6,7 +6,7 @@ $site-layout--breakpoint-sticky-footer: $breakpoint-small !default;
6
6
  .l-site {
7
7
  display: flex;
8
8
  flex-direction: column;
9
- min-height: 100vh;
9
+ min-height: 100dvh;
10
10
  }
11
11
 
12
12
  .l-footer--sticky {
@@ -843,8 +843,8 @@ $navigation-height: calc(map-get($settings-text-p, line-height) + 2 * $spv--larg
843
843
 
844
844
  .p-navigation--sliding.has-menu-open,
845
845
  .p-navigation--reduced.has-menu-open {
846
- box-shadow: $colors--theme--background-overlay 0px 0px 0px 100vh;
847
- height: 100vh;
846
+ box-shadow: $colors--theme--background-overlay 0px 0px 0px 100dvh;
847
+ height: 100dvh;
848
848
  overflow-y: hidden;
849
849
  position: fixed;
850
850
  width: 100vw;
@@ -866,7 +866,7 @@ $navigation-height: calc(map-get($settings-text-p, line-height) + 2 * $spv--larg
866
866
  }
867
867
  .p-navigation__nav {
868
868
  display: block;
869
- height: calc(100vh - $navigation-height);
869
+ height: calc(100dvh - $navigation-height);
870
870
  overflow-x: hidden;
871
871
 
872
872
  .p-navigation__items {
@@ -893,7 +893,7 @@ $navigation-height: calc(map-get($settings-text-p, line-height) + 2 * $spv--larg
893
893
  .p-navigation--sliding .p-navigation__dropdown,
894
894
  .p-navigation--reduced .p-navigation__dropdown {
895
895
  display: block;
896
- height: calc(100vh - $navigation-height);
896
+ height: calc(100dvh - $navigation-height);
897
897
  left: 100vw;
898
898
  position: absolute;
899
899
  top: 0;
@@ -1007,6 +1007,7 @@ $navigation-height: calc(map-get($settings-text-p, line-height) + 2 * $spv--larg
1007
1007
  .p-navigation__dropdown > :last-child {
1008
1008
  // should be enough to make some space at the bottom
1009
1009
  // and workaround the issues of 100vh not taking address toolbar into account
1010
+ // this workaround might not be needed since we moved from 100vh to 100dvh
1010
1011
  padding-bottom: 3rem;
1011
1012
 
1012
1013
  @media (min-width: $breakpoint-navigation-threshold) {
@@ -240,7 +240,7 @@
240
240
  // make whole navigation sticky on large screens
241
241
  .p-side-navigation.is-sticky,
242
242
  [class*='p-side-navigation--'].is-sticky {
243
- max-height: 100vh;
243
+ max-height: 100dvh;
244
244
  overflow-y: auto;
245
245
  position: sticky;
246
246
  top: 0;
@@ -0,0 +1,181 @@
1
+ const SUPPORTED_THEMES = ['light', 'dark', 'paper'];
2
+ const [DEFAULT_COLOR_THEME] = SUPPORTED_THEMES;
3
+ const COLOR_THEME_QUERY_PARAM_NAME = 'theme';
4
+ var activeTheme = DEFAULT_COLOR_THEME;
5
+
6
+ (function () {
7
+ function inIframe() {
8
+ try {
9
+ return window.self !== window.top;
10
+ } catch (e) {
11
+ return true;
12
+ }
13
+ }
14
+
15
+ function fragmentFromString(htmlString) {
16
+ var temp = document.createElement('template');
17
+ temp.innerHTML = htmlString;
18
+ return temp.content;
19
+ }
20
+
21
+ /**
22
+ * Gets the current query parameters
23
+ * @returns {URLSearchParams}
24
+ */
25
+ function getQueryParameters() {
26
+ return new URLSearchParams(window.location.search);
27
+ }
28
+
29
+ /**
30
+ * Sets the query parameter value of `key` to `value`
31
+ * @param {String} key
32
+ * @param {String} value
33
+ * @param {Boolean} reload Whether to cause a page load after updating the query parameter
34
+ * @returns {URLSearchParams} Query parameters after update
35
+ */
36
+ function setQueryParameter(key, value, reload = false) {
37
+ var currentQueryParams = getQueryParameters();
38
+
39
+ if (reload && currentQueryParams.get(key) !== value) {
40
+ currentQueryParams.set(key, value);
41
+ window.location.search = currentQueryParams.toString();
42
+ } else {
43
+ var url = new URL(window.location.href);
44
+ if (value) {
45
+ url.searchParams.set(key, value);
46
+ } else {
47
+ url.searchParams.delete(key);
48
+ }
49
+ if (window.location.href !== url.toString()) {
50
+ window.history.replaceState(null, null, url.toString());
51
+ }
52
+ }
53
+ return getQueryParameters();
54
+ }
55
+
56
+ /**
57
+ * Converts a theme name to its document class name, used for applying that color theme to the body
58
+ * @param {String} themeName
59
+ * @returns {string}
60
+ */
61
+ function convertThemeNameToClassName(themeName) {
62
+ return `is-${themeName}`;
63
+ }
64
+
65
+ /**
66
+ * Converts a theme name to its JS toggler identifier name, used for targeting it with JS events
67
+ * @param {String} themeName
68
+ * @returns {string}
69
+ */
70
+ function convertThemeNameToButtonIdentifier(themeName) {
71
+ return `js-${themeName}-theme-toggle`;
72
+ }
73
+
74
+ /**
75
+ * Converts a string to titlecase (first letter capitalized & subsequent letters lowercase)
76
+ * @param {String} str
77
+ * @returns {string}
78
+ */
79
+ function titleCase(str) {
80
+ return `${str.charAt(0).toUpperCase()}${str.slice(1).toLowerCase()}`;
81
+ }
82
+
83
+ /**
84
+ * Sets a color theme as active; removes all other color themes from active status
85
+ * @param {String} themeToSelect
86
+ */
87
+ function selectColorTheme(themeToSelect) {
88
+ // Apply the color theme to the document body
89
+ document.body.classList.add(convertThemeNameToClassName(themeToSelect));
90
+ // Remove the old color theme from the document body
91
+ if (themeToSelect !== activeTheme) document.body.classList.remove(convertThemeNameToClassName(activeTheme));
92
+
93
+ // Update address bar to reflect the newly selected color theme
94
+ setQueryParameter(COLOR_THEME_QUERY_PARAM_NAME, themeToSelect.toLowerCase());
95
+
96
+ // Update theme selector button states to reflect which one is currently active
97
+ var themeButtonToSelect = document.getElementById(convertThemeNameToButtonIdentifier(themeToSelect));
98
+ themeButtonToSelect?.setAttribute('aria-selected', 'true');
99
+ if (activeTheme) {
100
+ var themeButtonToDeselect = document.getElementById(convertThemeNameToButtonIdentifier(activeTheme));
101
+ themeButtonToDeselect?.setAttribute('aria-selected', 'false');
102
+ }
103
+
104
+ activeTheme = themeToSelect;
105
+ }
106
+
107
+ if (!inIframe()) {
108
+ document.documentElement.classList.add('u-baseline-grid');
109
+
110
+ document.addEventListener('DOMContentLoaded', function () {
111
+ var body = document.body;
112
+ var controls = document.createElement('div');
113
+ controls.classList.add('p-example-controls', 'p-form');
114
+ var queryParameters = getQueryParameters();
115
+ var requestedTheme = queryParameters.get(COLOR_THEME_QUERY_PARAM_NAME);
116
+ if (SHOW_THEME_SWITCH) {
117
+ // Some examples (i.e., button / dark) are pre-themed by their jinja template.
118
+ // if this is the case we don't modify the body class (it's already set); we just mark that theme as active.
119
+ var preExistingClassFromTemplate = SUPPORTED_THEMES.find((themeName) => body.classList.contains(convertThemeNameToClassName(themeName)));
120
+
121
+ if (preExistingClassFromTemplate) {
122
+ activeTheme = preExistingClassFromTemplate;
123
+ } else if (!requestedTheme) {
124
+ // No template-defined theme & no query-param-requested theme; fallback to default
125
+ selectColorTheme(DEFAULT_COLOR_THEME);
126
+ }
127
+
128
+ if (requestedTheme) {
129
+ if (SUPPORTED_THEMES.includes(requestedTheme)) {
130
+ selectColorTheme(requestedTheme);
131
+ } else {
132
+ // Query param used to request a theme that is not supported
133
+ selectColorTheme(DEFAULT_COLOR_THEME);
134
+ }
135
+ }
136
+
137
+ if (SUPPORTED_THEMES?.length > 1) {
138
+ var themeSwitcherControls = SUPPORTED_THEMES.map(
139
+ (themeName) =>
140
+ `<button id="${convertThemeNameToButtonIdentifier(themeName)}" class="p-segmented-control__button p-theme-toggle__button is-dense" role="button" aria-selected="${body.classList.contains(convertThemeNameToClassName(themeName))}" data-color-theme-name="${themeName}">${titleCase(themeName)}</button>`,
141
+ );
142
+ var themeSwitcherSegmentedControl = fragmentFromString(
143
+ `<div class="p-segmented-control u-theme-toggle"><div class="p-segmented-control__list">${themeSwitcherControls.join('')}</div></div>`,
144
+ );
145
+
146
+ controls.appendChild(themeSwitcherSegmentedControl);
147
+ }
148
+ } else if (requestedTheme) {
149
+ setQueryParameter(COLOR_THEME_QUERY_PARAM_NAME, null);
150
+ }
151
+ var baselineGridControl = fragmentFromString(
152
+ '<div class="u-baseline-grid__toggle"><label class="p-switch"><input type="checkbox" class="p-switch__input js-baseline-toggle" /><span class="p-switch__slider"></span><span class="p-switch__label">Toggle baseline grid</span></label></div>',
153
+ );
154
+ controls.appendChild(baselineGridControl);
155
+
156
+ const closeButtonFragment = fragmentFromString(`
157
+ <button class="p-button is-dense p-example-controls__close-button" id="js-example-toolbar-close-button">
158
+ Close
159
+ </button>
160
+ `);
161
+ controls.appendChild(closeButtonFragment);
162
+ body.appendChild(controls);
163
+
164
+ var closeButton = document.getElementById('js-example-toolbar-close-button');
165
+ closeButton.addEventListener('click', () => controls.remove());
166
+
167
+ // Below code relies on the controls already existing in the DOM, so must come after `body.appendChild`.
168
+ var themeToggleButtons = document.querySelectorAll('.p-theme-toggle__button');
169
+ themeToggleButtons.forEach((themeToggleButton) => {
170
+ themeToggleButton.addEventListener('click', () => {
171
+ selectColorTheme(themeToggleButton.getAttribute('data-color-theme-name'));
172
+ });
173
+ });
174
+
175
+ var toggle = document.querySelector('.js-baseline-toggle');
176
+ toggle.addEventListener('click', function (event) {
177
+ body.classList.toggle('u-baseline-grid');
178
+ });
179
+ });
180
+ }
181
+ })();