diff --git a/docs/blog/0.38.md b/docs/blog/0.38.md index b5cb0214f11..d9424bbe0f5 100644 --- a/docs/blog/0.38.md +++ b/docs/blog/0.38.md @@ -38,7 +38,7 @@ Read more about how to configure Themes by visiting our docs page [here](../../r ## Available Time Ranges Rill has always treated time as an essential part of the product and with this release you will get even more capabilities around customizing selectable time periods and comparison periods. -The `dashboard.yaml` introduces a new property called [available_time_ranges](/reference/project-files/explore-dashboards) that allows you define time ranges in [this format](/developers/build/metrics-view/time-series/time-syntax). +The `dashboard.yaml` introduces a new property called [available_time_ranges](/reference/project-files/explore-dashboards) that allows you define time ranges in [this format](/reference/time-syntax/rill-iso-extensions). You can either define this on a dashboard level or roll it out to all our dashboards via project defaults. ## Bug Fixes and Misc diff --git a/docs/docs/developers/build/dashboards/customization.md b/docs/docs/developers/build/dashboards/customization.md index 63e7badad4e..a10c972f0f0 100644 --- a/docs/docs/developers/build/dashboards/customization.md +++ b/docs/docs/developers/build/dashboards/customization.md @@ -28,7 +28,7 @@ security: Default time range controls the data analyzed on initial page load. Setting the default time range improves user experience by setting it to the most frequently used period— in particular, avoiding `all time` if you have a large data source but only analyze more recent data. -The value must be either a valid [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) (for example, `PT12H` for 12 hours, `P1M` for 1 month, or `P26W` for 26 weeks) or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions). +The value must be either a valid [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) (for example, `PT12H` for 12 hours, `P1M` for 1 month, or `P26W` for 26 weeks) or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions). ### Default Dimensions @@ -73,7 +73,7 @@ defaults: ## Time Ranges -One of the more important configurations, available time ranges allow you to change the defaults in the time dropdown for periods to select. Updating this list allows users to quickly change between the most common analyses, like day over day, recent weeks, or period to date. The range must be a valid [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions). +One of the more important configurations, available time ranges allow you to change the defaults in the time dropdown for periods to select. Updating this list allows users to quickly change between the most common analyses, like day over day, recent weeks, or period to date. The range must be a valid [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions). ```yaml time_ranges: diff --git a/docs/docs/reference/project-files/_category_.yml b/docs/docs/reference/project-files/_category_.yml index 3a6d2a3edd2..846359dbac7 100644 --- a/docs/docs/reference/project-files/_category_.yml +++ b/docs/docs/reference/project-files/_category_.yml @@ -1,4 +1,4 @@ position: 00 label: Project Files -collapsible: true +collapsible: false collapsed: false diff --git a/docs/docs/reference/project-files/canvas-dashboards.md b/docs/docs/reference/project-files/canvas-dashboards.md index 164ca9600ed..e605cba37cc 100644 --- a/docs/docs/reference/project-files/canvas-dashboards.md +++ b/docs/docs/reference/project-files/canvas-dashboards.md @@ -96,11 +96,11 @@ _[array of oneOf]_ - Overrides the list of default time range selections availab ``` - - **option 1** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection + - **option 1** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection - **option 2** - _[object]_ - Object containing time range and comparison configuration - - **`range`** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection _(required)_ + - **`range`** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection _(required)_ - **`comparison_offsets`** - _[array of oneOf]_ - list of time comparison options for this time range selection (optional). Must be one of the [Rill ISO 8601 extensions](https://docs.rilldata.com/reference/rill-iso-extensions#extensions) diff --git a/docs/docs/reference/project-files/explore-dashboards.md b/docs/docs/reference/project-files/explore-dashboards.md index e9efe6f4ce5..ae9bd882689 100644 --- a/docs/docs/reference/project-files/explore-dashboards.md +++ b/docs/docs/reference/project-files/explore-dashboards.md @@ -144,11 +144,11 @@ _[array of oneOf]_ - Overrides the list of default time range selections availab ``` - - **option 1** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection + - **option 1** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection - **option 2** - _[object]_ - Object containing time range and comparison configuration - - **`range`** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection _(required)_ + - **`range`** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection _(required)_ - **`comparison_offsets`** - _[array of oneOf]_ - list of time comparison options for this time range selection (optional). Must be one of the [Rill ISO 8601 extensions](https://docs.rilldata.com/reference/rill-iso-extensions#extensions) diff --git a/docs/docs/reference/project-files/metrics-views.md b/docs/docs/reference/project-files/metrics-views.md index d0c7d44ea7e..9456a3eaae8 100644 --- a/docs/docs/reference/project-files/metrics-views.md +++ b/docs/docs/reference/project-files/metrics-views.md @@ -346,11 +346,11 @@ _[object]_ - Defines an optional inline explore view for the metrics view. If no - **`time_ranges`** - _[array of oneOf]_ - Overrides the list of default time range selections available in the dropdown. It can be string or an object with a 'range' and optional 'comparison_offsets'. - - **option 1** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection + - **option 1** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection - **option 2** - _[object]_ - Object containing time range and comparison configuration - - **`range`** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection _(required)_ + - **`range`** - _[string]_ - a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection _(required)_ - **`comparison_offsets`** - _[array of oneOf]_ - list of time comparison options for this time range selection (optional). Must be one of the [Rill ISO 8601 extensions](https://docs.rilldata.com/reference/rill-iso-extensions#extensions) diff --git a/docs/docs/reference/time-syntax/_category_.yml b/docs/docs/reference/time-syntax/_category_.yml new file mode 100644 index 00000000000..49c16e94ec8 --- /dev/null +++ b/docs/docs/reference/time-syntax/_category_.yml @@ -0,0 +1,4 @@ +position: 200 +label: "Time Syntax" +collapsible: false +collapsed: false \ No newline at end of file diff --git a/docs/docs/developers/build/metrics-view/time-series/time-syntax.md b/docs/docs/reference/time-syntax/rill-iso-extensions.md similarity index 96% rename from docs/docs/developers/build/metrics-view/time-series/time-syntax.md rename to docs/docs/reference/time-syntax/rill-iso-extensions.md index 5be46c4c8ec..2be2f4361b3 100644 --- a/docs/docs/developers/build/metrics-view/time-series/time-syntax.md +++ b/docs/docs/reference/time-syntax/rill-iso-extensions.md @@ -1,7 +1,7 @@ --- -title: Time Syntax +title: Rill ISO Extensions description: Rill extensions to ISO 8601 time syntax -sidebar_label: Time Syntax +sidebar_label: Rill ISO Extensions sidebar_position: 10 --- diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 0c1991a4ce5..777274eb347 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -142,18 +142,31 @@ const config = { activeBaseRegex: "^/guide.*", // Keep Docs active for all doc pages }, { - to: "/reference/project-files", + type: "dropdown", label: "Reference", position: "left", - className: "navbar-reference-link", - activeBasePath: "/reference/project-files", - }, - { - to: "/api/admin/", - label: "API", - position: "left", - className: "navbar-api-link", - activeBasePath: "/api/admin/", + to: "/reference/project-files", + className: 'my-custom-dropdown', + activeBaseRegex: "^(/reference|/api/admin)", + items: [ + { + to: "/reference/project-files", + label: "Project Files", + }, + { + to: "/reference/cli", + label: "CLI", + }, + { + to: "/reference/time-syntax/rill-iso-extensions", + label: "Rill ISO 8601", + }, + { + to: "/api/admin/", + label: "REST API", + }, + + ], }, // { @@ -172,8 +185,7 @@ const config = { to: "/contact", label: "Contact Us", position: "left", - className: "navbar-contact-link", - activeBasePath: "/contact", + activeBaseRegex: "^/contact", }, @@ -1458,7 +1470,7 @@ const config = { }, { from: '/reference/rill-iso-extensions', - to: '/developers/build/metrics-view/time-series/time-syntax', + to: '/reference/time-syntax/rill-iso-extensions', }, { from: '/reference/olap-engines/', diff --git a/docs/src/css/_navbar.scss b/docs/src/css/_navbar.scss index 41be3b0c1c8..28f1ebaea51 100644 --- a/docs/src/css/_navbar.scss +++ b/docs/src/css/_navbar.scss @@ -1,269 +1,440 @@ +/* ========================= + Navbar Styles + + Class hooks added by Navbar/index.js: + - .dropdown--custom: Dropdown with custom chevron (has .my-custom-dropdown link) + - .navbar__item--has-icon-link: Navbar item containing .navbar-icon-link + - .navbar__dropdown--has-active: Dropdown with active menu item + - .navbar__item--has-active-link: Non-dropdown item with active link + - .menu__list-item--has-icon-link: Mobile menu item with icon link + - .navbar-sidebar__item--has-icon-link: Sidebar item with icon link + ========================= */ /* ========================= - Search Bar Custom Styles + Base Navbar Link Styles ========================= */ -.navbar { - height: 60px; - padding: 0 16px; -} -// all of the navbar items -.navbar__link { - height: 70%; - display: flex; - align-items: center; - justify-content: center; - padding: 0px 12px; - position: relative; - transition: color 0.2s ease; /* Smooth text color transition */ -} +.navbar .navbar__link { + color: var(--ifm-color-black); + text-decoration: none; + font-weight: 500; + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + transition: background-color 0.15s ease; -// on hover of navbar items -.navbar__link:hover { - background: var(--palette_slate_50); - border-radius: 8px; - padding: 12px 12px; -} + &:hover { + background-color: rgba(0, 0, 0, 0.03); + } -// Dark mode hover adjustment -[data-theme='dark'] .navbar__link:hover { - background: rgba(255, 255, 255, 0.1); + &.navbar__link--active:hover { + background-color: transparent; + } } -// Remove hover background for active items -.navbar__link--active:hover { - background: transparent; +/* Active navbar link styling */ +.navbar .navbar__link--active, +.navbar__item .navbar__link--active { + color: var(--ifm-color-primary); + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 14px; + text-decoration-color: var(--ifm-color-primary); + text-decoration-style: solid; + text-decoration-skip-ink: none; + text-decoration-skip: none; } -// Keep default color on hover (don't change text color) -.navbar__link.navbar-docs-link:hover, -.navbar__link.navbar-user-guide-link:hover, -.navbar__link.navbar-reference-link:hover, -.navbar__link.navbar-contact-link:hover, -.navbar__link.navbar-api-link:hover { - color: var(--ifm-navbar-link-color); +/* ========================= + Hide Default Caret for Custom Dropdowns + The caret is added via ::after pseudo-element by Infima CSS on .dropdown > .navbar__link + ========================= */ +.navbar .dropdown > .navbar__link.my-custom-dropdown::after { + content: none !important; } -// But if it's active, keep the primary color even when hovering -.navbar__link--active.navbar-docs-link:hover, -.navbar__link--active.navbar-user-guide-link:hover, -.navbar__link--active.navbar-reference-link:hover, -.navbar__link--active.navbar-contact-link:hover, -.navbar__link--active.navbar-api-link:hover { - color: var(--ifm-color-primary); -} +/* ========================= + Dropdown Container Styles + ========================= */ +.navbar .navbar__item.dropdown { + padding: 0; + margin: 0; + position: relative; -.navbar button[class*="toggleButton"] { - background: transparent; - border: none; - box-shadow: none; -} + /* Consolidate padding on the link element */ + > .navbar__link { + position: relative; + padding: 0.5rem 0.75rem; + padding-right: 2rem; /* Space for chevron */ + margin: 0; + } -.navbar button[class*="toggleButton"]:hover { - background: transparent; - box-shadow: none; -} + /* Ensure caret buttons have no extra spacing */ + > .menu__caret, + > button.menu__caret { + padding: 0; + margin: 0; + } -/* Active state styling for navbar links */ -.navbar__link--active.navbar-docs-link, -.navbar__link--active.navbar-user-guide-link, -.navbar__link--active.navbar-reference-link, -.navbar__link--active.navbar-contact-link, -.navbar__link--active.navbar-api-link, -.navbar__link--active.navbar-icon-link { - color: var(--ifm-color-primary); -} + /* Hover bridge - extends hover area to dropdown menu */ + &::before { + content: ''; + position: absolute; + bottom: -8px; + left: 0; + right: 0; + height: 8px; + background: transparent; + pointer-events: auto; + z-index: 1000; + } -/* Underline that only spans text width */ -.navbar__link--active.navbar-docs-link::after, -.navbar__link--active.navbar-user-guide-link::after, -.navbar__link--active.navbar-reference-link::after, -.navbar__link--active.navbar-contact-link::after, -.navbar__link--active.navbar-api-link::after { - content: ''; - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - height: 2px; - background: var(--ifm-color-primary); - border-radius: 1px 1px 0 0; - width: calc(100% - 24px); + /* Active dropdown styling */ + &.navbar__dropdown--has-active > .navbar__link { + color: var(--ifm-color-primary); + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 14px; + text-decoration-color: var(--ifm-color-primary); + text-decoration-style: solid; + text-decoration-skip-ink: none; + text-decoration-skip: none; + } } +/* ========================= + Custom Dropdown Styles (dropdown--custom) + ========================= */ +.navbar .navbar__item.dropdown.dropdown--custom { + /* Custom chevron positioning and animation */ + .navbar__link.my-custom-dropdown { + position: relative; + + .custom-chevron { + position: absolute; + right: 0.75rem; + top: 30%; + transform: translateY(0%); + transition: transform 0.2s ease; + opacity: 0.7; + width: 14px; + height: 14px; + display: block; + pointer-events: none; + + svg { + width: 14px; + height: 14px; + display: block; + } + } + } -// NavBar Icon Link (GitHub and Blog) Change to Icons later -// NavBar Icon Link (GitHub and Blog) Change to Icons later -.navbar-icon-link { - width: 24px; - height: 24px; - margin-right: 6px; - margin-left: 6px; - text-decoration: none; - color: var(--ifm-menu-color); - font-size: 0.875rem; - font-weight: 500; - transition: color 0.2s; - display: inline-flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; + /* Rotate chevron on hover/open */ + &:hover .navbar__link.my-custom-dropdown .custom-chevron, + &.dropdown--show .navbar__link.my-custom-dropdown .custom-chevron { + transform: translateY(0%) rotate(180deg); + } + + .navbar__link.my-custom-dropdown[aria-expanded="true"] .custom-chevron { + transform: translateY(0%) rotate(180deg); + } +} + +/* ========================= + Dropdown Menu Styles + ========================= */ +.navbar .dropdown__menu { + margin-top: 8px; + margin-left: 0; } -.navbar-icon-link:hover { - background: transparent; - box-shadow: none; +.navbar .dropdown__link { + color: var(--ifm-color-black); text-decoration: none; - color: var(--ifm-menu-color); + border-radius: 0.375rem; + padding: 0.375rem 0.75rem; + display: block; + + &:hover, + &:focus { + color: var(--ifm-color-black); + text-decoration: none; + background-color: rgba(0, 0, 0, 0.05); + } + + &.dropdown__link--active { + color: var(--ifm-color-primary); + text-decoration: none; + font-weight: 600; + } } -// NavBar Search Bar -.navbar .navbar__items--right .DocSearch-Button { - background: transparent; - border: 0.5px solid #e5e7eb; - border-radius: 8px; - padding: 12px 12px; - width: 250px; /* Adjust this value to change width */ - min-width: 150px; /* Minimum width */ - height: 100%; - position: relative; - - &::after { - content: "⌘K"; - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - font-size: 0.75rem; - color: var(--docsearch-muted-color); - font-family: monospace; +/* ========================= + Dropdown Closing State + Used by DropdownNavbarItem.js on route change + ========================= */ +.navbar.navbar--closing-dropdown .navbar__item.dropdown { + &::before { pointer-events: none; } -} -// Dark mode specific fixes for Search Bar -[data-theme='dark'] .navbar .navbar__items--right .DocSearch-Button { - background: transparent; - border: 0.5px solid var(--docsearch-muted-color); - border-radius: 8px; - padding: 12px 12px; + .dropdown__menu { + display: none; + opacity: 0; + visibility: hidden; + pointer-events: none; + } } -[data-theme='dark'] .navbar .navbar__items--right .DocSearch-Button:hover { - box-shadow: none; - color: var(--docsearch-muted-color); - border: 0.5px solid var(--ifm-color-primary); -} - -// On hover of Search Bar -.navbar .navbar__items--right .DocSearch-Button:hover { - box-shadow: none; - color: var(--docsearch-muted-color); - border: 0.5px solid var(--ifm-color-primary); +/* ========================= + Icon Link Styles (navbar__item--has-icon-link) + Replaces :has(.navbar-icon-link) selectors + ========================= */ +.navbar .navbar__item.navbar__item--has-icon-link { + padding: 0; } -// Search Bar Search Icon -.navbar .navbar__items--right .DocSearch-Search-Icon { - height: 16px; - width: 16px; +.navbar .navbar-icon-link { + color: var(--ifm-color-black); + text-decoration: none; + font-weight: 500; + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + transition: background-color 0.15s ease; + + &:hover, + &:focus { + color: var(--ifm-color-black); + text-decoration: none; + background-color: rgba(0, 0, 0, 0.03); + } } -// Search Bar Search Placeholder -.navbar .navbar__items--right .DocSearch-Button-Placeholder { +/* ========================= + DocSearch Button Styles + ========================= */ +.navbar .DocSearch-Button { font-size: 0.875rem; -} + min-width: 16rem; + border: 0.5px solid var(--ifm-color-gray-300, #e5e7eb); -.navbar .navbar__items--right .DocSearch-Button-Keys { - display: none; -} + .DocSearch-Button-Placeholder { + font-size: 0.875rem; + } -.DocSearch--active .DocSearch-Footer { - display: none; -} + @media (max-width: 996px) { + min-width: 12rem; + font-size: 0.8125rem; + .DocSearch-Button-Placeholder { + font-size: 0.8125rem; + } + } + @media (max-width: 576px) { + min-width: 10rem; + font-size: 0.75rem; -@media (max-width: 996px) { - /* Make custom HTML navbar items look like menu__link in mobile menu */ - .navbar .menu__list .menu__link, - .navbar .menu__list .navbar-icon-link { - display: flex; - align-items: center; - width: 100%; - gap: 6px; - padding: 2.5px 9px; - color: var(--ifm-menu-color); - font-size: var(--default-font); - text-decoration: none; - border-radius: 4px; - margin: 0; - background: none; - box-shadow: none; + .DocSearch-Button-Placeholder { + font-size: 0.75rem; + } } +} - .navbar .navbar__items--left .navbar-icon-link { - width: 100%; - display: flex; - align-items: center; +/* ========================= + Mobile/Tablet Breakpoint Styles + ========================= */ +@media (max-width: 996px) { + .navbar .navbar__item.navbar__item--has-icon-link { + padding: 0; } .navbar .navbar-icon-link { - display: none !important; + padding: 0.5rem 0.75rem; + + &:hover, + &:focus { + color: var(--ifm-color-black); + background-color: rgba(0, 0, 0, 0.03); + } + } +} + +/* ========================= + Dark Mode Styles + ========================= */ +[data-theme='dark'] .navbar { + .navbar__link { + color: var(--ifm-color-white); + + &:hover, + &:focus { + color: var(--ifm-color-white); + background-color: rgba(255, 255, 255, 0.06); + } + + &.navbar__link--active:hover { + background-color: transparent; + } } -} + .navbar__item--has-active-link .navbar__link, + .navbar__item .navbar__link--active, + .navbar__link--active { + color: var(--ifm-color-primary); + } -@media (max-width: 768px) { - .navbar .navbar__item { - display: none + .navbar__item.dropdown.navbar__dropdown--has-active > .navbar__link { + color: var(--ifm-color-primary); } -} -@media (max-width: 600px) { - .navbar .navbar__items--right .DocSearch-Button { - width: 100%; - min-width: 0; + .dropdown__menu { + margin-top: 8px; margin-left: 0; - height: 36px; - box-sizing: border-box; } - .navbar .navbar__items--right .DocSearch-Button::after { - display: none !important; + + .dropdown__link { + color: var(--ifm-color-white); + + &:hover, + &:focus { + color: var(--ifm-color-white); + background-color: rgba(255, 255, 255, 0.1); + } + + &.dropdown__link--active { + color: var(--ifm-color-primary); + font-weight: 600; + } + } + + .navbar-icon-link { + color: var(--ifm-color-white); + + &:hover, + &:focus { + color: var(--ifm-color-white); + background-color: rgba(255, 255, 255, 0.06); + } + } + + .DocSearch-Button { + border: 0.5px solid rgba(255, 255, 255, 0.2); + } + + @media (max-width: 996px) { + .navbar-icon-link { + &:hover, + &:focus { + background-color: transparent; + } + } + + .navbar__item .DocSearch-Button:hover { + background-color: rgba(255, 255, 255, 0.1); + } } } +/* ========================= + Mobile Sidebar Panel Styles + ========================= */ +.navbar-sidebar { + .navbar-sidebar__item { + margin: 0; + padding: 0; + } + .navbar-sidebar__item.dropdown { + .dropdown__menu { + padding: 0.5rem 0; + margin: 0; + } + + .dropdown__link { + padding: 0.625rem 1.5rem 0.625rem 1.75rem; + margin: 0.125rem 0; + border-radius: 0.375rem; + } + } -/* Hide text on very small screens for right-side items */ -@media (max-width: 480px) { - .navbar .navbar__items--right .navbar-icon-link { - display: none; + /* DocSearch button in sidebar */ + .navbar-sidebar__item .DocSearch-Button { + padding: 0.5rem 0.75rem; + margin: 0.25rem 0; + border-radius: 0.375rem; + width: 100%; + max-width: 100%; + justify-content: flex-start; + font-size: 0.8125rem; + min-width: auto; + + .DocSearch-Button-Placeholder { + font-size: 0.8125rem; + } + + @media (max-width: 576px) { + padding: 0.375rem 0.5rem; + font-size: 0.75rem; + + .DocSearch-Button-Placeholder { + font-size: 0.75rem; + } + } } -} -//Mobile Hamburger Menu Header + /* Icon links in sidebar - using class hooks */ + .navbar-sidebar__item--has-icon-link, + .menu__list-item--has-icon-link { + background: none; -.navbar-sidebar__brand { - height: 60px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 16px; -} + &:hover { + background: none; + } -.navbar-sidebar__brand .mobile-nav-icon-links { - display: flex; - align-items: center; - gap: 24px; -} + .menu__link { + background: none; -.navbar-sidebar__brand .mobile-nav-icon-link { - display: inline-flex; - align-items: center; - justify-content: center; + &:hover { + background: none; + } + } + } + + .navbar-sidebar__item .navbar-icon-link { + display: block; + padding: 0.25rem 0.5rem 0.25rem 0; + color: var(--ifm-color-black); + text-decoration: none; + font-weight: 500; + font-size: var(--default-font); + + &:hover, + &:focus { + color: var(--ifm-color-primary); + font-weight: 600; + background: none; + } + } + + /* Consistent spacing */ + .navbar-sidebar__items { + padding: 1rem 0.5rem; + + > .navbar-sidebar__item { + margin-bottom: 0.25rem; + } + } } -.navbar-sidebar__brand .mobile-nav-icon-link img { - width: 100%; - height: 100%; +/* Dark mode for mobile sidebar */ +[data-theme='dark'] .navbar-sidebar { + .navbar-sidebar__item .navbar-icon-link { + color: var(--ifm-color-white); + + &:hover, + &:focus { + color: var(--ifm-color-primary); + font-weight: 600; + } + } } diff --git a/docs/src/theme/Navbar/index.js b/docs/src/theme/Navbar/index.js index 20c9deffc37..9b512eb190f 100644 --- a/docs/src/theme/Navbar/index.js +++ b/docs/src/theme/Navbar/index.js @@ -1,69 +1,41 @@ import { useColorMode } from '@docusaurus/theme-common'; +import { useLocation } from '@docusaurus/router'; import Navbar from '@theme-original/Navbar'; import { useEffect, useLayoutEffect } from 'react'; -const MOBILE_ICON_LINKS = [ - { - href: 'https://github.com/rilldata/rill', - label: 'GitHub', - src: '/icons/Github.svg', - }, - { - href: '/notes', - label: 'Release Notes', - src: '/icons/ReleaseNotes.svg', - }, - { - href: 'https://www.rilldata.com/blog', - label: 'Blog', - src: '/icons/MessageSquareQuote.svg', - }, -]; - -function createIconLink({ href, label, src }) { - const anchor = document.createElement('a'); - anchor.href = href; - anchor.target = '_blank'; - anchor.rel = 'noopener noreferrer'; - anchor.className = 'mobile-nav-icon-link'; - anchor.setAttribute('aria-label', label); - - const img = document.createElement('img'); - img.src = src; - img.alt = label; - img.width = 24; - img.height = 24; - - anchor.appendChild(img); - return anchor; -} - -function ensureSidebarIcons() { - const brand = document.querySelector('.navbar-sidebar__brand'); - // Check if icons already exist - if (!brand || brand.querySelector('.mobile-nav-icon-links')) { - return; - } - - const container = document.createElement('div'); - container.className = 'mobile-nav-icon-links'; - - MOBILE_ICON_LINKS.forEach((link) => { - container.appendChild(createIconLink(link)); - }); - - const closeButton = brand.querySelector('.navbar-sidebar__close'); - if (closeButton) { - brand.insertBefore(container, closeButton); - } else { - brand.appendChild(container); - } -} +// Mobile icon functionality removed export default function NavbarWrapper(props) { // We only need colorMode to determine which icon to show. // The toggle logic is handled by the original button's onClick. const { colorMode } = useColorMode(); + const location = useLocation(); + + // Close all open dropdowns when route changes + // This fixes the issue where hover-based dropdowns stay open after clicking an item + useEffect(() => { + const closeAllDropdowns = () => { + // Remove dropdown--show class from all dropdowns + const openDropdowns = document.querySelectorAll('.navbar__item.dropdown.dropdown--show'); + openDropdowns.forEach((dropdown) => { + dropdown.classList.remove('dropdown--show'); + }); + + // Also reset aria-expanded attributes + const expandedLinks = document.querySelectorAll('.navbar__link[aria-expanded="true"]'); + expandedLinks.forEach((link) => { + link.setAttribute('aria-expanded', 'false'); + }); + + // Blur any focused navbar elements to prevent hover state from re-triggering + const activeElement = document.activeElement; + if (activeElement && activeElement.closest('.navbar__item.dropdown')) { + activeElement.blur(); + } + }; + + closeAllDropdowns(); + }, [location.pathname]); // Handle Dark Mode Toggle Icons // useLayoutEffect fires synchronously before paint, reducing flicker @@ -80,9 +52,9 @@ export default function NavbarWrapper(props) { if (!iconContainer) { iconContainer = document.createElement('span'); iconContainer.className = 'icon-container'; - + // Clear existing Docusaurus toggle content (text/emojis) - btn.innerHTML = ''; + btn.innerHTML = ''; btn.appendChild(iconContainer); } @@ -90,7 +62,7 @@ export default function NavbarWrapper(props) { // If Dark Mode -> Show Sun (to switch to Light) // If Light Mode -> Show Moon (to switch to Dark) const isDark = colorMode === 'dark'; - + iconContainer.innerHTML = ` `; - + btn.setAttribute('aria-label', isDark ? 'Switch to light mode' : 'Switch to dark mode'); }); }, [colorMode]); - // Handle Mobile Sidebar Icons + // Mobile sidebar icons removed - no longer needed + + // Consolidated MutationObserver for navbar updates useEffect(() => { - // Observer to inject icons when the mobile menu opens/renders - const observer = new MutationObserver(() => { - ensureSidebarIcons(); - }); + const markActiveDropdowns = () => { + // Target elements with both navbar__item and dropdown classes + const dropdownItems = document.querySelectorAll('.navbar__item.dropdown'); + dropdownItems.forEach((dropdownItem) => { + const activeDropdownLink = dropdownItem.querySelector('.dropdown__link--active'); + if (activeDropdownLink) { + dropdownItem.classList.add('navbar__dropdown--has-active'); + } else { + dropdownItem.classList.remove('navbar__dropdown--has-active'); + } + }); + }; + const markActiveNavItems = () => { + // Target non-dropdown navbar items + const navItems = document.querySelectorAll('.navbar__item:not(.dropdown)'); + navItems.forEach((navItem) => { + const activeLink = navItem.querySelector('.navbar__link--active'); + if (activeLink) { + navItem.classList.add('navbar__item--has-active-link'); + } else { + navItem.classList.remove('navbar__item--has-active-link'); + } + }); + }; + + const addDataTextAttributes = () => { + const navLinks = document.querySelectorAll('.navbar__link'); + navLinks.forEach((link) => { + // Only add if not already present + if (!link.hasAttribute('data-text')) { + const text = link.textContent?.trim() || ''; + if (text) { + link.setAttribute('data-text', text); + } + } + }); + }; + + const markCustomDropdowns = () => { + // Add class hook for dropdowns with custom dropdown links (replaces :has(.my-custom-dropdown)) + const customDropdownLinks = document.querySelectorAll('.navbar__link.my-custom-dropdown'); + customDropdownLinks.forEach((link) => { + const dropdownItem = link.closest('.navbar__item.dropdown'); + if (dropdownItem) { + dropdownItem.classList.add('dropdown--custom'); + } + }); + }; + + const markIconLinkItems = () => { + // Add class hook for navbar items containing icon links (replaces :has(.navbar-icon-link)) + const iconLinks = document.querySelectorAll('.navbar-icon-link'); + iconLinks.forEach((link) => { + const navItem = link.closest('.navbar__item'); + if (navItem) { + navItem.classList.add('navbar__item--has-icon-link'); + } + // Also mark parent list items in mobile sidebar + const menuListItem = link.closest('.menu__list-item'); + if (menuListItem) { + menuListItem.classList.add('menu__list-item--has-icon-link'); + } + // Mark sidebar items + const sidebarItem = link.closest('.navbar-sidebar__item'); + if (sidebarItem) { + sidebarItem.classList.add('navbar-sidebar__item--has-icon-link'); + } + }); + }; + + const replaceCustomDropdownCarets = () => { + // Add custom SVG chevron for custom dropdown links + const customDropdownLinks = document.querySelectorAll('.navbar__link.my-custom-dropdown'); + customDropdownLinks.forEach((link) => { + const dropdownItem = link.closest('.navbar__item.dropdown'); + if (dropdownItem && !link.hasAttribute('data-custom-chevron-added')) { + // Mark as processed + link.setAttribute('data-custom-chevron-added', 'true'); + + // Create a container for the custom chevron + let chevronContainer = link.querySelector('.custom-chevron'); + if (!chevronContainer) { + chevronContainer = document.createElement('span'); + chevronContainer.className = 'custom-chevron'; + link.appendChild(chevronContainer); + } + + // Clear and add custom SVG chevron + chevronContainer.innerHTML = ''; + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('height', '14px'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('fill', 'currentColor'); + svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); + + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('fill-rule', 'evenodd'); + path.setAttribute('clip-rule', 'evenodd'); + path.setAttribute('d', 'M19.189 9.43683C19.3842 9.63209 19.3842 9.94867 19.189 10.1439L11.9999 17.333L4.81075 10.1439C4.61549 9.94867 4.61549 9.63209 4.81075 9.43683L5.98898 8.2586C6.18424 8.06334 6.50082 8.06334 6.69609 8.2586L11.9999 13.5624L17.3036 8.2586C17.4989 8.06334 17.8155 8.06334 18.0108 8.2586L19.189 9.43683Z'); + + svg.appendChild(path); + chevronContainer.appendChild(svg); + } + }); + }; + + // Combined update function + const updateNavbar = () => { + markActiveDropdowns(); + markActiveNavItems(); + markCustomDropdowns(); + markIconLinkItems(); + addDataTextAttributes(); + replaceCustomDropdownCarets(); + }; + + // Run on mount and when DOM changes + updateNavbar(); + + // Staggered timeouts to handle dynamically rendered navbar elements. + // Docusaurus may hydrate or lazy-load navbar items at different times, + // especially for dropdowns and client-side navigation. These delays + // ensure our custom styling (data-text attrs, active states, chevrons) + // is applied even if elements render after initial mount. + setTimeout(updateNavbar, 100); + setTimeout(updateNavbar, 500); + setTimeout(updateNavbar, 1000); + + const observer = new MutationObserver(updateNavbar); observer.observe(document.body, { childList: true, subtree: true }); - ensureSidebarIcons(); // Initial check return () => { observer.disconnect(); - const existing = document.querySelector('.mobile-nav-icon-links'); - if (existing) { - existing.remove(); - } }; }, []); diff --git a/docs/src/theme/NavbarItem/DropdownNavbarItem.js b/docs/src/theme/NavbarItem/DropdownNavbarItem.js new file mode 100644 index 00000000000..da1a5c3536e --- /dev/null +++ b/docs/src/theme/NavbarItem/DropdownNavbarItem.js @@ -0,0 +1,65 @@ +import React, { useEffect, useCallback } from 'react'; +import DropdownNavbarItem from '@theme-original/NavbarItem/DropdownNavbarItem'; +import { useLocation } from '@docusaurus/router'; + +/** + * Swizzled DropdownNavbarItem component that closes dropdown on item click. + * + * This fixes the issue where hover-based dropdowns stay open after clicking + * a menu item because the ::before pseudo-element (hover bridge) keeps the + * dropdown open if the mouse is still in that area. + */ +export default function DropdownNavbarItemWrapper(props) { + const location = useLocation(); + + // Helper to close all dropdowns and temporarily disable hover + const closeAllDropdowns = useCallback(() => { + // Add a class to the navbar to temporarily disable hover + const navbar = document.querySelector('.navbar'); + if (navbar) { + navbar.classList.add('navbar--closing-dropdown'); + } + + // Remove dropdown--show class from all dropdowns + const openDropdowns = document.querySelectorAll('.navbar__item.dropdown.dropdown--show'); + openDropdowns.forEach((dropdown) => { + dropdown.classList.remove('dropdown--show'); + }); + + // Reset aria-expanded attributes and blur + const expandedLinks = document.querySelectorAll('.navbar__link[aria-expanded="true"]'); + expandedLinks.forEach((link) => { + link.setAttribute('aria-expanded', 'false'); + link.blur(); + }); + + // Remove the closing class after mouse has likely moved away + setTimeout(() => { + if (navbar) { + navbar.classList.remove('navbar--closing-dropdown'); + } + }, 300); + }, []); + + // Close dropdown when route changes + useEffect(() => { + closeAllDropdowns(); + }, [location.pathname, closeAllDropdowns]); + + // Global click handler for dropdown links + useEffect(() => { + const handleClick = (e) => { + const dropdownLink = e.target.closest('.dropdown__link'); + if (dropdownLink) { + // Immediately close + closeAllDropdowns(); + } + }; + + // Use capture phase to catch events before they bubble + document.addEventListener('click', handleClick, true); + return () => document.removeEventListener('click', handleClick, true); + }, [closeAllDropdowns]); + + return ; +} diff --git a/runtime/parser/schema/project.schema.yaml b/runtime/parser/schema/project.schema.yaml index ea824a60e35..df3b7694aca 100644 --- a/runtime/parser/schema/project.schema.yaml +++ b/runtime/parser/schema/project.schema.yaml @@ -3286,13 +3286,13 @@ definitions: explore_time_range_properties: oneOf: - type: string - description: a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection + description: a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection - type: object description: Object containing time range and comparison configuration properties: range: type: string - description: a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/developers/build/metrics-view/time-series/time-syntax#extensions) extensions for the selection + description: a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration or one of the [Rill ISO 8601 extensions](/reference/time-syntax/rill-iso-extensions#extensions) extensions for the selection comparison_offsets: type: array description: list of time comparison options for this time range selection (optional). Must be one of the [Rill ISO 8601 extensions](https://docs.rilldata.com/reference/rill-iso-extensions#extensions)