-
- Custom content
-
-
-
-
-
- Custom content
-
-
-
-
-
- Custom content
-
-
+export const SecondaryOnlyIcon = () => (
+
+
)
-CustomContent.story = {
- name: 'Custom content',
+export const SecondaryDropdown = () => {
+ const ref = useRef()
+
+ return (
+
+
+
+ )
}
-export const Toggle = () =>
+export const SecondaryOnlyIconDropdown = () => {
+ const ref = useRef()
+
+ return (
+
+
+
+ )
+}
diff --git a/pkg/webui/components/dropdown-v2/dropdown.styl b/pkg/webui/components/dropdown-v2/dropdown.styl
deleted file mode 100644
index aa1b0e9207..0000000000
--- a/pkg/webui/components/dropdown-v2/dropdown.styl
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-ul.dropdown
- font-size: $fs.m
- border-radius: $br.l
- border: 1px solid $c.grey-200
- list-style-type: none
- background: $c.white
- box-shadow: 0px 3px 16px 0px rgba(0, 0, 0, 0.06)
- position: absolute
- padding: $cs.s
- min-width: 14rem
- z-index: $zi.dropdown
- margin: $cs.xxs 0 0
- opacity: 0
- visibility: hidden
- transition: opacity 0.2s ease-in-out, visibility 0s linear 0.2s
-
- &.open
- opacity: 1
- visibility: visible
- transition: opacity 0.2s ease-in-out, visibility 0s linear
-
- &.below
- bottom: auto
- top: calc(100% + 7px)
-
- &.above
- top: auto
- bottom: calc(100% + 7px)
-
- &.left
- left: 0
- right: auto
-
- &.right
- right: 0
- left: auto
-
- &.example
- bottom: auto
- top: 0
- left: 0
- right: auto
-
- &.larger
- li.dropdown-item
- & > a.button, & > button.button
- padding: $cs.l
-
- hr
- height: 1px
- background-color: $c.grey-200
-
- li.dropdown-header-item
- display: block
- margin-bottom: 0
- font-weight: $fw.bold
-
- span
- line-height: 1
- display: block
- padding: $cs.m
-
- li.dropdown-item
- margin-bottom: 0
- text-align: left
-
- button.button
- reset-button()
-
- & > a.button, & > button.button
- box-sizing: border-box
- line-height: 1
- display: flex
- align-items: center
- text-decoration: none
- padding: $cs.xs
- width: 100%
- min-height: 2.429rem
- &:not(.active)
- color: $c.grey-700
-
- .icon
- color: $c.grey-700
-
- &.active
- border-radius: $br.m
- background: $c.tts-primary-150
- transition: background 0.1s ease-in-out
- color: $tc-deep-gray
-
- .icon
- color: $tc-deep-gray
-
- &:hover
- color: $tc-deep-gray
- background: $c.tts-primary-150
- transition: background 0.1s ease-in-out
- border-radius: $br.m
-
- .icon
- color: $tc-deep-gray
-
- .icon
- margin-right: $cs.xs
-
- &:hover
- color: $tc-deep-gray
- text-decoration: none
-
- .submenu-container
- bottom: 0px
- left: 0px
- position: absolute
- width: 180%
- height: 5rem
- z-index: 9999
-
- .submenu-dropdown
- bottom: -0px !important
- left: calc(100% + -213px) !important
diff --git a/pkg/webui/components/dropdown-v2/index.js b/pkg/webui/components/dropdown-v2/index.js
deleted file mode 100644
index 8cf1d800ec..0000000000
--- a/pkg/webui/components/dropdown-v2/index.js
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React, { useCallback, useEffect, useRef, useState } from 'react'
-import classnames from 'classnames'
-import { NavLink } from 'react-router-dom'
-
-import Icon from '@ttn-lw/components/icon'
-import Link from '@ttn-lw/components/link'
-
-import Message from '@ttn-lw/lib/components/message'
-
-import PropTypes from '@ttn-lw/lib/prop-types'
-
-import style from './dropdown.styl'
-
-const Dropdown = ({ className, children, larger, onItemsClick, open }) => {
- const ref = useRef(null)
- const [isBelow, setIsBelow] = useState(false)
- const [isOnRight, setIsOnRight] = useState(false)
-
- const positionDropdown = useCallback(() => {
- if (ref.current) {
- const parentRect = ref.current.parentElement.getBoundingClientRect()
- const spaceBelow = window.innerHeight - parentRect.bottom
- const spaceAbove = parentRect.top
- const dropdownHeight = ref.current.clientHeight
- const dropdownWidth = ref.current.clientWidth
- const spaceOnLeft = parentRect.left
-
- setIsBelow(spaceBelow > dropdownHeight || spaceAbove < dropdownHeight)
-
- setIsOnRight(spaceOnLeft > dropdownWidth)
- }
- }, [])
-
- useEffect(() => {
- if (open) positionDropdown()
- }, [positionDropdown, open])
-
- useEffect(() => {
- window.addEventListener('scroll', positionDropdown)
- window.addEventListener('resize', positionDropdown)
-
- return () => {
- window.removeEventListener('resize', positionDropdown)
- window.removeEventListener('scroll', positionDropdown)
- }
- }, [positionDropdown])
-
- return (
-
- )
-}
-
-Dropdown.propTypes = {
- children: PropTypes.node.isRequired,
- className: PropTypes.string,
- larger: PropTypes.bool,
- onItemsClick: PropTypes.func,
- open: PropTypes.bool.isRequired,
-}
-
-Dropdown.defaultProps = {
- className: undefined,
- larger: false,
- onItemsClick: () => null,
-}
-
-const DropdownItem = ({
- active,
- icon,
- title,
- path,
- action,
- exact,
- showActive,
- tabIndex,
- external,
- submenuItems,
- ...rest
-}) => {
- const [expandedSubmenu, setExpandedSubmenu] = useState(false)
- const iconElement = icon &&
- const activeClassName = classnames({
- [style.active]: (!Boolean(action) && showActive) || active,
- })
- const cls = useCallback(
- ({ isActive }) => classnames(style.button, { [activeClassName]: isActive }),
- [activeClassName],
- )
- const ItemElement = action ? (
-
- {iconElement}
-
-
- ) : external ? (
-
- {Boolean(iconElement) ? iconElement : null}
-
-
- ) : (
-
- {iconElement}
-
-
- )
-
- const handleMouseEnter = useCallback(() => {
- setExpandedSubmenu(true)
- }, [setExpandedSubmenu])
-
- const handleMouseLeave = useCallback(() => {
- setExpandedSubmenu(false)
- }, [setExpandedSubmenu])
-
- const withSubmenu = (
-
-
- {iconElement}
-
-
-
- {expandedSubmenu ? (
-
-
- {submenuItems}
-
-
- ) : null}
-
- )
-
- return (
-
- {Boolean(submenuItems) ? withSubmenu : ItemElement}
-
- )
-}
-
-DropdownItem.propTypes = {
- action: PropTypes.func,
- active: PropTypes.bool,
- exact: PropTypes.bool,
- external: PropTypes.bool,
- icon: PropTypes.string,
- path: PropTypes.string,
- showActive: PropTypes.bool,
- submenuItems: PropTypes.arrayOf(PropTypes.node),
- tabIndex: PropTypes.string,
- title: PropTypes.message.isRequired,
-}
-
-DropdownItem.defaultProps = {
- active: false,
- action: undefined,
- exact: false,
- external: false,
- icon: undefined,
- path: undefined,
- showActive: true,
- tabIndex: '0',
- submenuItems: undefined,
-}
-
-const DropdownHeaderItem = ({ title }) => (
-
-
-
-
-
-)
-
-DropdownHeaderItem.propTypes = {
- title: PropTypes.message.isRequired,
-}
-
-Dropdown.Item = DropdownItem
-Dropdown.HeaderItem = DropdownHeaderItem
-
-export default Dropdown
diff --git a/pkg/webui/components/dropdown/dropdown.styl b/pkg/webui/components/dropdown/dropdown.styl
index ef553c6f83..aa1b0e9207 100644
--- a/pkg/webui/components/dropdown/dropdown.styl
+++ b/pkg/webui/components/dropdown/dropdown.styl
@@ -1,4 +1,4 @@
-// Copyright © 2020 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -13,15 +13,47 @@
// limitations under the License.
ul.dropdown
- border-normal()
- z-index: $zi.nav
- right: 0
+ font-size: $fs.m
+ border-radius: $br.l
+ border: 1px solid $c.grey-200
+ list-style-type: none
+ background: $c.white
+ box-shadow: 0px 3px 16px 0px rgba(0, 0, 0, 0.06)
position: absolute
- background: white
- padding: 0
+ padding: $cs.s
min-width: 14rem
z-index: $zi.dropdown
- margin: 0
+ margin: $cs.xxs 0 0
+ opacity: 0
+ visibility: hidden
+ transition: opacity 0.2s ease-in-out, visibility 0s linear 0.2s
+
+ &.open
+ opacity: 1
+ visibility: visible
+ transition: opacity 0.2s ease-in-out, visibility 0s linear
+
+ &.below
+ bottom: auto
+ top: calc(100% + 7px)
+
+ &.above
+ top: auto
+ bottom: calc(100% + 7px)
+
+ &.left
+ left: 0
+ right: auto
+
+ &.right
+ right: 0
+ left: auto
+
+ &.example
+ bottom: auto
+ top: 0
+ left: 0
+ right: auto
&.larger
li.dropdown-item
@@ -29,9 +61,8 @@ ul.dropdown
padding: $cs.l
hr
- margin-top: 0
- height: .1rem
- background-color: $c-divider
+ height: 1px
+ background-color: $c.grey-200
li.dropdown-header-item
display: block
@@ -44,8 +75,8 @@ ul.dropdown
padding: $cs.m
li.dropdown-item
- display: block
margin-bottom: 0
+ text-align: left
button.button
reset-button()
@@ -53,25 +84,51 @@ ul.dropdown
& > a.button, & > button.button
box-sizing: border-box
line-height: 1
- display: block
+ display: flex
+ align-items: center
text-decoration: none
- padding: $cs.m
+ padding: $cs.xs
width: 100%
+ min-height: 2.429rem
&:not(.active)
- color: $tc-deep-gray
+ color: $c.grey-700
+
+ .icon
+ color: $c.grey-700
&.active
- border-left: $c-active-blue 5px solid
- color: $c-active-blue
+ border-radius: $br.m
+ background: $c.tts-primary-150
+ transition: background 0.1s ease-in-out
+ color: $tc-deep-gray
+
+ .icon
+ color: $tc-deep-gray
&:hover
- color: white
+ color: $tc-deep-gray
+ background: $c.tts-primary-150
+ transition: background 0.1s ease-in-out
+ border-radius: $br.m
.icon
- color: white
+ color: $tc-deep-gray
.icon
margin-right: $cs.xs
&:hover
- background-color: $c-active-blue
+ color: $tc-deep-gray
+ text-decoration: none
+
+ .submenu-container
+ bottom: 0px
+ left: 0px
+ position: absolute
+ width: 180%
+ height: 5rem
+ z-index: 9999
+
+ .submenu-dropdown
+ bottom: -0px !important
+ left: calc(100% + -213px) !important
diff --git a/pkg/webui/components/dropdown/index.js b/pkg/webui/components/dropdown/index.js
index 1a39a90e3a..8cf1d800ec 100644
--- a/pkg/webui/components/dropdown/index.js
+++ b/pkg/webui/components/dropdown/index.js
@@ -1,4 +1,4 @@
-// Copyright © 2020 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import React, { useCallback } from 'react'
+import React, { useCallback, useEffect, useRef, useState } from 'react'
import classnames from 'classnames'
import { NavLink } from 'react-router-dom'
@@ -25,21 +25,64 @@ import PropTypes from '@ttn-lw/lib/prop-types'
import style from './dropdown.styl'
-const Dropdown = React.forwardRef(({ className, children, larger, onItemsClick }, ref) => (
-
-))
+const Dropdown = ({ className, children, larger, onItemsClick, open }) => {
+ const ref = useRef(null)
+ const [isBelow, setIsBelow] = useState(false)
+ const [isOnRight, setIsOnRight] = useState(false)
+
+ const positionDropdown = useCallback(() => {
+ if (ref.current) {
+ const parentRect = ref.current.parentElement.getBoundingClientRect()
+ const spaceBelow = window.innerHeight - parentRect.bottom
+ const spaceAbove = parentRect.top
+ const dropdownHeight = ref.current.clientHeight
+ const dropdownWidth = ref.current.clientWidth
+ const spaceOnLeft = parentRect.left
+
+ setIsBelow(spaceBelow > dropdownHeight || spaceAbove < dropdownHeight)
+
+ setIsOnRight(spaceOnLeft > dropdownWidth)
+ }
+ }, [])
+
+ useEffect(() => {
+ if (open) positionDropdown()
+ }, [positionDropdown, open])
+
+ useEffect(() => {
+ window.addEventListener('scroll', positionDropdown)
+ window.addEventListener('resize', positionDropdown)
+
+ return () => {
+ window.removeEventListener('resize', positionDropdown)
+ window.removeEventListener('scroll', positionDropdown)
+ }
+ }, [positionDropdown])
+
+ return (
+
+ )
+}
Dropdown.propTypes = {
children: PropTypes.node.isRequired,
className: PropTypes.string,
larger: PropTypes.bool,
onItemsClick: PropTypes.func,
+ open: PropTypes.bool.isRequired,
}
Dropdown.defaultProps = {
@@ -58,8 +101,10 @@ const DropdownItem = ({
showActive,
tabIndex,
external,
+ submenuItems,
...rest
}) => {
+ const [expandedSubmenu, setExpandedSubmenu] = useState(false)
const iconElement = icon &&
const activeClassName = classnames({
[style.active]: (!Boolean(action) && showActive) || active,
@@ -90,9 +135,39 @@ const DropdownItem = ({
)
+
+ const handleMouseEnter = useCallback(() => {
+ setExpandedSubmenu(true)
+ }, [setExpandedSubmenu])
+
+ const handleMouseLeave = useCallback(() => {
+ setExpandedSubmenu(false)
+ }, [setExpandedSubmenu])
+
+ const withSubmenu = (
+
+
+ {iconElement}
+
+
+
+ {expandedSubmenu ? (
+
+
+ {submenuItems}
+
+
+ ) : null}
+
+ )
+
return (
- {ItemElement}
+ {Boolean(submenuItems) ? withSubmenu : ItemElement}
)
}
@@ -105,6 +180,7 @@ DropdownItem.propTypes = {
icon: PropTypes.string,
path: PropTypes.string,
showActive: PropTypes.bool,
+ submenuItems: PropTypes.arrayOf(PropTypes.node),
tabIndex: PropTypes.string,
title: PropTypes.message.isRequired,
}
@@ -118,6 +194,7 @@ DropdownItem.defaultProps = {
path: undefined,
showActive: true,
tabIndex: '0',
+ submenuItems: undefined,
}
const DropdownHeaderItem = ({ title }) => (
diff --git a/pkg/webui/components/dropdown-v2/story.js b/pkg/webui/components/dropdown/story.js
similarity index 100%
rename from pkg/webui/components/dropdown-v2/story.js
rename to pkg/webui/components/dropdown/story.js
diff --git a/pkg/webui/components/header-v2/header-v2.styl b/pkg/webui/components/header-v2/header-v2.styl
deleted file mode 100644
index 4f555e5b5b..0000000000
--- a/pkg/webui/components/header-v2/header-v2.styl
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-.container
- padding: 0 $cs.m
- background: $c['tts-primary-050']
- z-index: $zi.nav
- border-bottom: 1px solid $c-divider
- display: flex
- justify-content: space-between
- align-items: center
- height: 4rem
-
-.logo
- height: $cs.l
diff --git a/pkg/webui/components/header-v2/index.js b/pkg/webui/components/header-v2/index.js
deleted file mode 100644
index d1180edde3..0000000000
--- a/pkg/webui/components/header-v2/index.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React, { useRef } from 'react'
-import classnames from 'classnames'
-
-import { Breadcrumbs } from '@ttn-lw/components/breadcrumbs/breadcrumbs'
-import Button from '@ttn-lw/components/button-v2'
-import ProfileDropdown from '@ttn-lw/components/profile-dropdown-v2'
-import Link from '@ttn-lw/components/link'
-
-import PropTypes from '@ttn-lw/lib/prop-types'
-
-import style from './header-v2.styl'
-
-const Header = ({
- brandLogo,
- logo,
- breadcrumbs,
- className,
- addDropdownItems,
- starDropdownItems,
- profileDropdownItems,
- user,
- onMenuClick,
- ...rest
-}) => {
- const addRef = useRef(null)
- const starRef = useRef(null)
-
- // Const isGuest = !Boolean(user)
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {profileDropdownItems}
-
-
-
- )
-}
-
-const imgPropType = PropTypes.shape({
- src: PropTypes.string.isRequired,
- alt: PropTypes.string.isRequired,
-})
-
-Header.propTypes = {
- /** The dropdown items when the add button is clicked. */
- addDropdownItems: PropTypes.node.isRequired,
- brandLogo: imgPropType,
- /** A list of breadcrumb elements. */
- breadcrumbs: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.element]))
- .isRequired,
- /** The classname applied to the component. */
- className: PropTypes.string,
- logo: imgPropType.isRequired,
- /** A handler for when the menu button is clicked. */
- onMenuClick: PropTypes.func.isRequired,
- /** The dropdown items when the profile button is clicked. */
- profileDropdownItems: PropTypes.node.isRequired,
- /** The dropdown items when the star button is clicked. */
- starDropdownItems: PropTypes.node.isRequired,
- /**
- * The User object, retrieved from the API. If it is `undefined`, then the
- * guest header is rendered.
- */
- user: PropTypes.user,
-}
-
-Header.defaultProps = {
- className: undefined,
- user: undefined,
- brandLogo: undefined,
-}
-
-export default Header
diff --git a/pkg/webui/components/header-v2/story.js b/pkg/webui/components/header-v2/story.js
deleted file mode 100644
index 5da2147ecc..0000000000
--- a/pkg/webui/components/header-v2/story.js
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React from 'react'
-
-import TtsLogo from '@assets/static/tts-logo.svg'
-
-import Dropdown from '@ttn-lw/components/dropdown-v2'
-import ExampleLogo from '@ttn-lw/components/logo/story-logo-new.svg'
-import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
-
-import Header from '.'
-
-const user = {
- name: 'johndoe',
- ids: {
- user_id: 'jdoe300',
- },
-}
-
-export default {
- title: 'Header V2',
-}
-
-const plusDropdownItems = (
- <>
-
-
-
-
- >
-)
-
-const starDropdownItems = (
- <>
-
-
-
-
-
-
- >
-)
-
-const profileDropdownItems = (
- <>
-
-
-
-
-
-
-
-
-
-
- >
-)
-
-export const Default = () => {
- const breadcrumbs = [
-
,
-
,
-
,
- ]
-
- return (
-
-
-
- )
-}
diff --git a/pkg/webui/components/header/header.styl b/pkg/webui/components/header/header.styl
index 9433fef1fb..4f555e5b5b 100644
--- a/pkg/webui/components/header/header.styl
+++ b/pkg/webui/components/header/header.styl
@@ -1,4 +1,4 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,128 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-mobile-styles($breakpoint)
- +media-query($breakpoint)
- &.mobile-menu-open
- position: fixed
- z-index: $zi.mobile-menu
- top: 0
- left: 0
- height: 100vh
- background-color: white
-
- .hamburger img
- width: 1.5rem
-
- .mobile-menu-button
- display: block
-
- .nav-list
- display: none
-
- .profile-dropdown
- display: none
-
- .mobile-menu
- display: flex
-
- +media-query-between($bp.s, $breakpoint)
- .mobile-menu
- height: 'calc(100vh - %s)' % $header-height
-
- +media-query($bp.s)
- z-index: $zi.mobile-menu
- position: fixed
- top: 0
-
- & + main
- padding-top: $header-height
- +media-query($bp.s)
- padding-top: $header-height-mobile
-
- .bar
- height: $header-height-mobile
- background-color: white
- padding: 0 $ls.xxs
-
- .mobile-menu
- height: 'calc(100vh - %s)' % $header-height-mobile
- top: $header-height-mobile
-
- .mobile-menu-button
- width: $header-height-mobile
- margin-right: $ls.xxs * -1
-
.container
- mobile-styles($bp.m)
- background-color: white
+ padding: 0 $cs.m
+ background: $c['tts-primary-050']
z-index: $zi.nav
- width: 100%
-
-.bar
- border-normal('bottom')
- background: linear-gradient(bottom left, 0% #F8F8F8, 60% white) // @stylint ignore
- height: $header-height
- box-sizing: border-box
- position: relative
+ border-bottom: 1px solid $c-divider
display: flex
- padding: 0 $ls.s
justify-content: space-between
- flex: none
-
-.logo
- display: inline-flex
align-items: center
- justify-content: flex-start
+ height: 4rem
- img
- nudge('up')
-
-.nav-list
- display: flex
- align-items: stretch
- justify-content: flex-start
- padding: 0 $ls.m
- flex-grow: 2
- height: 100%
- max-width: 56rem
- flex-shrink: 0
-
-.profile-dropdown
- margin-right: -2rem
-
-.mobile-menu-button
- reset-button()
- display: none
- width: $header-height
- margin-right: $ls.s * -1
- height: 100%
- position: relative
-
- .hamburger
- center-absolute()
- display: block
- width: 2rem
- height: 100%
-
- img
- center-absolute()
- width: 2rem
-
-.left
- display: flex
- align-items: center
- max-width: 42rem
- flex-grow: 1
-
-.right
- padding-left: $cs.l
- display: flex
- align-items: center
- justify-content: flex-end
- max-width: 25rem
- flex-grow: 2
-
- & > div:first-child
- max-width: 15rem
- border-color: $c-divider-dark
+.logo
+ height: $cs.l
diff --git a/pkg/webui/components/header/index.js b/pkg/webui/components/header/index.js
index d7af0b5d8a..fbe07991a8 100644
--- a/pkg/webui/components/header/index.js
+++ b/pkg/webui/components/header/index.js
@@ -1,4 +1,4 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,107 +12,87 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import React, { useState, useCallback } from 'react'
+import React, { useRef } from 'react'
import classnames from 'classnames'
-import hamburgerMenuNormal from '@assets/misc/hamburger-menu-normal.svg'
-import hamburgerMenuClose from '@assets/misc/hamburger-menu-close.svg'
-
-import NavigationBar from '@ttn-lw/components/navigation/bar'
+import Button from '@ttn-lw/components/button'
import ProfileDropdown from '@ttn-lw/components/profile-dropdown'
-import MobileMenu from '@ttn-lw/components/mobile-menu'
-import Input from '@ttn-lw/components/input'
+import Link from '@ttn-lw/components/link'
import PropTypes from '@ttn-lw/lib/prop-types'
import style from './header.styl'
const Header = ({
+ brandLogo,
+ logo,
+ breadcrumbs,
className,
- dropdownItems,
- navigationEntries,
+ addDropdownItems,
+ starDropdownItems,
+ profileDropdownItems,
user,
- searchable,
- logo,
- mobileDropdownItems,
- onLogout,
- onSearchRequest,
+ onMenuClick,
...rest
}) => {
- const isGuest = !Boolean(user)
-
- const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
- const handleMobileMenuClick = useCallback(() => {
- setMobileMenuOpen(!mobileMenuOpen)
- }, [mobileMenuOpen])
-
- const handleMobileMenuItemsClick = useCallback(() => {
- setMobileMenuOpen(false)
- }, [])
+ const addRef = useRef(null)
+ const starRef = useRef(null)
- const classNames = classnames(className, style.container, {
- [style.mobileMenuOpen]: mobileMenuOpen,
- })
-
- const hamburgerGraphic = mobileMenuOpen ? hamburgerMenuClose : hamburgerMenuNormal
+ // Const isGuest = !Boolean(user)
return (
-
)
}
+const imgPropType = PropTypes.shape({
+ src: PropTypes.string.isRequired,
+ alt: PropTypes.string.isRequired,
+})
+
Header.propTypes = {
+ /** The dropdown items when the add button is clicked. */
+ addDropdownItems: PropTypes.node.isRequired,
+ brandLogo: imgPropType,
+ /** A list of breadcrumb elements. */
+ breadcrumbs: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.element]))
+ .isRequired,
/** The classname applied to the component. */
className: PropTypes.string,
- /** The child node of the dropdown component. */
- dropdownItems: PropTypes.node,
- /** The logo component. */
- logo: PropTypes.node.isRequired,
- /** The child node of the mobile dropdown. */
- mobileDropdownItems: PropTypes.node,
- /** The Child node of the navigation bar. */
- navigationEntries: PropTypes.node,
- /** A handler for when the user used the search input. */
- onLogout: PropTypes.func,
- /** Handler of the search function. */
- onSearchRequest: PropTypes.func,
- /* A flag indicating whether the header has a search input. */
- searchable: PropTypes.bool,
+ logo: imgPropType.isRequired,
+ /** A handler for when the menu button is clicked. */
+ onMenuClick: PropTypes.func.isRequired,
+ /** The dropdown items when the profile button is clicked. */
+ profileDropdownItems: PropTypes.node.isRequired,
+ /** The dropdown items when the star button is clicked. */
+ starDropdownItems: PropTypes.node.isRequired,
/**
* The User object, retrieved from the API. If it is `undefined`, then the
* guest header is rendered.
@@ -122,13 +102,8 @@ Header.propTypes = {
Header.defaultProps = {
className: undefined,
- dropdownItems: undefined,
- navigationEntries: undefined,
- mobileDropdownItems: null,
- onSearchRequest: () => null,
- onLogout: () => null,
- searchable: false,
user: undefined,
+ brandLogo: undefined,
}
export default Header
diff --git a/pkg/webui/components/header/story.js b/pkg/webui/components/header/story.js
index 3d1610df6c..4c48bbb031 100644
--- a/pkg/webui/components/header/story.js
+++ b/pkg/webui/components/header/story.js
@@ -1,4 +1,4 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -13,70 +13,83 @@
// limitations under the License.
import React from 'react'
-import { action } from '@storybook/addon-actions'
-import TtsLogo from '@assets/static/logo.svg'
+import TtsLogo from '@assets/static/tts-logo.svg'
import Dropdown from '@ttn-lw/components/dropdown'
-import NavigationBar from '@ttn-lw/components/navigation/bar'
-import Logo from '@ttn-lw/components/logo'
-import ExampleLogo from '@ttn-lw/components/logo/story-logo.svg'
+import ExampleLogo from '@ttn-lw/components/logo/story-logo-new.svg'
+import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
import Header from '.'
const user = {
- name: 'kschiffer',
+ name: 'johndoe',
ids: {
- user_id: 'ksc300',
+ user_id: 'jdoe300',
},
}
-const singleLogo =
-const doubleLogo = (
-
-)
+export default {
+ title: 'Header V2',
+}
-const navigationEntries = (
-
-
-
-
-
-
+const plusDropdownItems = (
+ <>
+
+
+
+
+ >
)
-const items = (
-
-
-
-
+const starDropdownItems = (
+ <>
+
+
+
+
+
+
+ >
)
-export default {
- title: 'Header',
-}
-
-export const SingleLogo = () => (
-
+const profileDropdownItems = (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
)
-export const DoubleLogo = () => (
-
-)
+export const Default = () => {
+ const breadcrumbs = [
+
,
+
,
+
,
+ ]
+
+ return (
+
+
+
+ )
+}
diff --git a/pkg/webui/components/navigation/side-v2/index.js b/pkg/webui/components/navigation/side-v2/index.js
deleted file mode 100644
index 4b895e3948..0000000000
--- a/pkg/webui/components/navigation/side-v2/index.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React, { useRef } from 'react'
-import classnames from 'classnames'
-
-import PropTypes from '@ttn-lw/lib/prop-types'
-
-import SideNavigationList from './list'
-import SideNavigationItem from './item'
-
-import style from './side.styl'
-
-const SideNavigation = ({ className, children }) => {
- const node = useRef()
-
- const navigationClassNames = classnames(className, style.navigation)
-
- return (
-
- {children}
-
- )
-}
-
-SideNavigation.propTypes = {
- children: PropTypes.node.isRequired,
- className: PropTypes.string,
-}
-
-SideNavigation.defaultProps = {
- className: undefined,
-}
-
-SideNavigation.Item = SideNavigationItem
-
-export default SideNavigation
diff --git a/pkg/webui/components/navigation/side-v2/item/index.js b/pkg/webui/components/navigation/side-v2/item/index.js
deleted file mode 100644
index a87411fd0b..0000000000
--- a/pkg/webui/components/navigation/side-v2/item/index.js
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React, { useCallback, useEffect, useState } from 'react'
-import classnames from 'classnames'
-
-import Dropdown from '@ttn-lw/components/dropdown-v2'
-import MenuLink from '@ttn-lw/components/sidebar/side-menu-link'
-import Button from '@ttn-lw/components/button-v2'
-import Icon from '@ttn-lw/components/icon'
-
-import Message from '@ttn-lw/lib/components/message'
-
-import PropTypes from '@ttn-lw/lib/prop-types'
-
-import SideNavigationList from '../list'
-
-import style from './item.styl'
-
-const handleItemClick = event => {
- if (event && event.target) {
- event.target.blur()
- }
-}
-
-const SideNavigationItem = props => {
- const { className, children, title, depth, icon, path, exact, isActive, isMinimized } = props
- const [isExpanded, setIsExpanded] = useState(false)
-
- const handleExpandCollapsableItem = useCallback(() => {
- setIsExpanded(isExpanded => !isExpanded)
- document.activeElement.blur()
- }, [])
-
- useEffect(() => {
- // Make sure that the item corresponding to the currently open path is expanded
- // on initial render, if applicable
- if (Boolean(children)) {
- const paths = React.Children.toArray(children).reduce(
- (paths, child) => [...paths, ...(Boolean(child) ? child.props.path : [])],
- [],
- )
- for (const path of paths) {
- if (location.pathname.includes(path)) {
- setIsExpanded(true)
- return
- }
- }
- }
- }, [children])
-
- return (
-
- {Boolean(children) ? (
-
- ) : (
-
- )}
-
- )
-}
-
-SideNavigationItem.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- depth: PropTypes.number,
- /** A flag specifying whether the path of the linkable item should be matched exactly or not. */
- exact: PropTypes.bool,
- /** The name of the icon for the side navigation item. */
- icon: PropTypes.string,
- /** A flag specifying whether the side navigation item is active or not. */
- isActive: PropTypes.bool,
- isMinimized: PropTypes.bool,
- /** The path of the linkable side navigation item. */
- path: PropTypes.string,
- /** The title of the side navigation item. */
- title: PropTypes.message.isRequired,
-}
-
-SideNavigationItem.defaultProps = {
- className: undefined,
- children: undefined,
- exact: false,
- icon: undefined,
- isActive: false,
- depth: 0,
- path: undefined,
- isMinimized: false,
-}
-
-const CollapsableItem = ({
- children,
- onClick,
- isExpanded,
- isMinimized,
- title,
- icon,
- depth,
- onDropdownItemsClick,
- currentPathName,
-}) => {
- const subItems = children
- .filter(item => Boolean(item) && 'props' in item)
- .map(item => ({
- title: item.props.title,
- path: item.props.path,
- icon: item.props.icon,
- }))
-
- const subItemActive = subItems.some(item => currentPathName.includes(item.path))
-
- return (
- <>
-
- {icon && }
- {!isMinimized && (
- <>
-
-
- >
- )}
- {isMinimized && (
-
-
-
- {subItems.map(item => (
-
- ))}
-
-
- )}
-
- {!isMinimized && (
-
- {children}
-
- )}
- >
- )
-}
-
-CollapsableItem.propTypes = {
- children: PropTypes.node,
- currentPathName: PropTypes.string.isRequired,
- depth: PropTypes.number.isRequired,
- icon: PropTypes.string,
- isExpanded: PropTypes.bool.isRequired,
- isMinimized: PropTypes.bool.isRequired,
- onClick: PropTypes.func.isRequired,
- onDropdownItemsClick: PropTypes.func,
- title: PropTypes.message.isRequired,
-}
-
-CollapsableItem.defaultProps = {
- children: undefined,
- icon: undefined,
- onDropdownItemsClick: () => null,
-}
-
-const LinkItem = ({ onClick, title, icon, exact, path }) => {
- const handleLinkItemClick = useCallback(
- event => {
- document.activeElement.blur()
- onClick(event)
- },
- [onClick],
- )
-
- return (
- <>
-
- >
- )
-}
-
-LinkItem.propTypes = {
- exact: PropTypes.bool.isRequired,
- icon: PropTypes.string,
- onClick: PropTypes.func,
- path: PropTypes.string,
- title: PropTypes.message.isRequired,
-}
-
-LinkItem.defaultProps = {
- icon: undefined,
- path: undefined,
- onClick: () => null,
-}
-
-export default SideNavigationItem
diff --git a/pkg/webui/components/navigation/side-v2/item/item.styl b/pkg/webui/components/navigation/side-v2/item/item.styl
deleted file mode 100644
index ae017616e8..0000000000
--- a/pkg/webui/components/navigation/side-v2/item/item.styl
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-.item
- &:hover
- .fly-out-list
- display: block
-
-.button
- reset-button()
- width: 100%
- transition: 0.2s background-color ease-in, 0.2s color ease-in
- text-decoration: none
- display: flex
- align-items: center
- position: relative
- gap: $cs.xs !important
-
- span
- &:not(:first-child)
- color: $c.grey-700 !important
-
- &:hover&:not(&-active)
- background: none !important
- color: $tc-deep-gray !important
- text-decoration: none
-
- span
- &:not(:first-child)
- color: $c.grey-900 !important
-
- .icon
- color: inherit
-
- &-active
- background: $c.tts-primary-150
- color: $c.grey-900 !important
-
- &:hover
- background: $c.tts-primary-150 !important
- color: $c.grey-900 !important
-
- span
- &:not(:first-child)
- color: $c.grey-900 !important
-
- .icon
- color: inherit
-
- .icon
- color: inherit
-
-.message
- vertical-align: middle
-
-.expand-icon
- position: absolute
- right: $cs.xs
- top: 50%
- transform: translateY(-50%)
- transition: transform $ad.m ease-in
-
- &-open
- transform: translateY(-50%) rotate(180deg)
-
-.fly-out-list-container
- top: 0
- left: 0
- position: absolute
- width: 180%
- height: 13rem
-
-.fly-out-list
- display: none
- left: 5rem !important
- top: 0 !important
diff --git a/pkg/webui/components/navigation/side-v2/list/index.js b/pkg/webui/components/navigation/side-v2/list/index.js
deleted file mode 100644
index 4561c58e4c..0000000000
--- a/pkg/webui/components/navigation/side-v2/list/index.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React from 'react'
-import classnames from 'classnames'
-
-import PropTypes from '@ttn-lw/lib/prop-types'
-
-import style from './list.styl'
-
-const SideNavigationList = ({ children, className, depth, isExpanded }) => {
- const isRoot = depth === 0
- const listClassNames = classnames(className, style.list, {
- [style.listNested]: !isRoot,
- [style.listExpanded]: isExpanded,
- })
-
- return
-}
-
-SideNavigationList.propTypes = {
- children: PropTypes.node.isRequired,
- className: PropTypes.string,
- /** The depth of the current list starting at 0 for the root list. */
- depth: PropTypes.number,
- /**
- * A flag specifying whether the side navigation list is expanded or not.
- * Applicable to nested lists.
- */
- isExpanded: PropTypes.bool,
-}
-
-SideNavigationList.defaultProps = {
- className: undefined,
- depth: 0,
- isExpanded: false,
-}
-
-export default SideNavigationList
diff --git a/pkg/webui/components/navigation/side-v2/list/list.styl b/pkg/webui/components/navigation/side-v2/list/list.styl
deleted file mode 100644
index baddbf7c23..0000000000
--- a/pkg/webui/components/navigation/side-v2/list/list.styl
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-.list
- list-style-type: none
- padding: 0
- margin: 0
- overflow: visible
- font-size: $fs.m
-
- & > li:not(:last-child)
- margin-bottom: 0
-
- // We need to rely on the child list items for the reveal animation, the
- // height of the list is variable, making proper slide up transitions
- // impossible.
- & > li
- transition: height $ad.m ease-in-out
-
- &-nested > li
- height: 0px
- transition: height $ad.m ease-in-out
-
- &-nested:not(.list-expanded) > li
- visibility: hidden
-
- &-expanded > li
- height: 2.4rem
-
- &-expanded, &-nested
- & li a
- padding-left: $cs.l + $cs.xs
-
-.icon
- margin-right: $cs.xs
diff --git a/pkg/webui/components/navigation/side-v2/side.styl b/pkg/webui/components/navigation/side-v2/side.styl
deleted file mode 100644
index 05670b0362..0000000000
--- a/pkg/webui/components/navigation/side-v2/side.styl
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-.container
- border-normal(right)
- display: flex
- flex-direction: column
- z-index: $zi.nav
- +media-query-min($bp.s)
- left: 0
- top: 0
- position: absolute
- height: 100%
- +media-query($bp.s)
- height: auto
- position: sticky
- top: $header-height-mobile
- z-index: $zi.nav
- +media-query-height(500px)
- position: static
-
-.navigation
- box-sizing: border-box
- max-height: 100%
- width: 100%
-
- &-minimized
- overflow: visible
- display: flex
- flex-direction: column
- justify-content: space-between
- +media-query-min($bp.s)
- width: $sidebar-width-minimized
- display: inline-flex
-
- .header
- padding: 0
- width: 100%
- display: flex
- height: $breadcrumbs-bar-height
- justify-content: center
-
- .message
- display: none
-
- .icon
- margin-right: 0
-
- .navigation-list
- overflow: visible
-
- .drawer
- overflow: visible
-
-.drawer
- background-color: white
- z-index: $zi.nav
- overflow: auto
- +media-query($bp.s)
- sidebar-transition(left, box-shadow)
- position: fixed
- top: $header-height-mobile
- left: -60vw
- bottom: 0
- width: 60vw
- max-width: 20rem
-
- &-open
- box-shadow: 0 0 13rem 2rem rgba(0,0,0,.4)
- left: 0
-
-.message
- display: block
- white-space: nowrap
- overflow: hidden
- text-overflow: ellipsis
-
-.scroll-lock
- +media-query($bp.s)
- overflow: hidden
diff --git a/pkg/webui/components/navigation/side-v2/story.js b/pkg/webui/components/navigation/side-v2/story.js
deleted file mode 100644
index baf5acf7cd..0000000000
--- a/pkg/webui/components/navigation/side-v2/story.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React from 'react'
-
-import SidebarContext from '@console/containers/side-bar/context'
-
-import SideNavigationItem from './item'
-
-import SideNavigation from '.'
-
-export default {
- title: 'Navigation v2',
- component: SideNavigation,
- decorators: [
- storyFn => (
-
{storyFn()}
- ),
- ],
-}
-
-export const _SideNavigation = () => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)
diff --git a/pkg/webui/components/navigation/side/context.js b/pkg/webui/components/navigation/side/context.js
deleted file mode 100644
index 6537cf3e33..0000000000
--- a/pkg/webui/components/navigation/side/context.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright © 2020 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React from 'react'
-
-const SideNavigationContext = React.createContext()
-
-export default SideNavigationContext
diff --git a/pkg/webui/components/navigation/side/index.js b/pkg/webui/components/navigation/side/index.js
index 77bafa2edc..4b895e3948 100644
--- a/pkg/webui/components/navigation/side/index.js
+++ b/pkg/webui/components/navigation/side/index.js
@@ -12,214 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import ReactDom from 'react-dom'
-import React, { useState, useEffect, useCallback, useRef } from 'react'
+import React, { useRef } from 'react'
import classnames from 'classnames'
-import { defineMessages, useIntl } from 'react-intl'
-
-import LAYOUT from '@ttn-lw/constants/layout'
-
-import Button from '@ttn-lw/components/button'
-import Icon from '@ttn-lw/components/icon'
-import Link from '@ttn-lw/components/link'
-
-import Message from '@ttn-lw/lib/components/message'
import PropTypes from '@ttn-lw/lib/prop-types'
import SideNavigationList from './list'
import SideNavigationItem from './item'
-import SideNavigationContext from './context'
import style from './side.styl'
-const getViewportWidth = () =>
- Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
-
-const m = defineMessages({
- hideSidebar: 'Hide sidebar',
-})
-
-const SideNavigation = ({
- appContainerId,
- modifyAppContainerClasses,
- className,
- header,
- children,
-}) => {
- const [isMinimized, setIsMinimized] = useState(getViewportWidth() <= LAYOUT.BREAKPOINTS.M)
- const [isDrawerOpen, setIsDrawerOpen] = useState(false)
- const [preferMinimized, setPreferMinimized] = useState(false)
+const SideNavigation = ({ className, children }) => {
const node = useRef()
- const intl = useIntl()
-
- const updateAppContainerClasses = useCallback(
- (initial = false) => {
- if (!modifyAppContainerClasses) {
- return
- }
- const containerClasses = document.getElementById(appContainerId).classList
- containerClasses.add('with-sidebar')
- if (!initial) {
- containerClasses.add('sidebar-transitioned')
- }
- if (isMinimized) {
- containerClasses.add('sidebar-minimized')
- } else {
- containerClasses.remove('sidebar-minimized')
- }
- },
- [modifyAppContainerClasses, appContainerId, isMinimized],
- )
-
- const removeAppContainerClasses = useCallback(() => {
- if (!modifyAppContainerClasses) {
- return
- }
- document
- .getElementById(appContainerId)
- .classList.remove('with-sidebar', 'sidebar-minimized', 'sidebar-transitioned')
- }, [modifyAppContainerClasses, appContainerId])
-
- const closeDrawer = useCallback(() => {
- setIsDrawerOpen(false)
- document.body.classList.remove(style.scrollLock)
- }, [])
-
- const openDrawer = useCallback(() => {
- setIsDrawerOpen(true)
- document.body.classList.add(style.scrollLock)
- }, [])
- useEffect(() => {
- const onClickOutside = e => {
- if (isDrawerOpen && node.current && !node.current.contains(e.target)) {
- closeDrawer()
- }
- }
-
- if (isDrawerOpen) {
- document.addEventListener('mousedown', onClickOutside)
- return () => document.removeEventListener('mousedown', onClickOutside)
- }
- }, [isDrawerOpen, closeDrawer])
-
- const setMinimizedState = useCallback(() => {
- const viewportWidth = getViewportWidth()
- if (
- (!isMinimized && viewportWidth <= LAYOUT.BREAKPOINTS.M) ||
- (isMinimized && viewportWidth > LAYOUT.BREAKPOINTS.M)
- ) {
- setIsMinimized(getViewportWidth() <= LAYOUT.BREAKPOINTS.M || preferMinimized)
- updateAppContainerClasses()
- }
- }, [isMinimized, preferMinimized, updateAppContainerClasses])
-
- useEffect(() => {
- window.addEventListener('resize', setMinimizedState)
- updateAppContainerClasses(true)
- return () => {
- window.removeEventListener('resize', setMinimizedState)
- removeAppContainerClasses()
- }
- }, [removeAppContainerClasses, setMinimizedState, updateAppContainerClasses])
-
- const onToggle = useCallback(async () => {
- setIsMinimized(prev => !prev)
- setPreferMinimized(prev => !prev)
- updateAppContainerClasses()
- }, [updateAppContainerClasses])
-
- const onDrawerExpandClick = useCallback(() => {
- if (!isDrawerOpen) {
- openDrawer()
- } else {
- closeDrawer()
- }
- }, [isDrawerOpen, openDrawer, closeDrawer])
-
- const onLeafItemClick = useCallback(() => {
- if (isDrawerOpen) {
- onDrawerExpandClick()
- }
- }, [isDrawerOpen, onDrawerExpandClick])
-
- const navigationClassNames = classnames(className, style.navigation, {
- [style.navigationMinimized]: isMinimized,
- })
- const minimizeButtonClassNames = classnames(style.minimizeButton, {
- [style.minimizeButtonMinimized]: isMinimized,
- })
-
- const drawerClassNames = classnames(style.drawer, { [style.drawerOpen]: isDrawerOpen })
+ const navigationClassNames = classnames(className, style.navigation)
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- >
+
+ {children}
+
)
}
SideNavigation.propTypes = {
- appContainerId: PropTypes.string,
children: PropTypes.node.isRequired,
className: PropTypes.string,
- /** The header for the side navigation. */
- header: PropTypes.shape({
- title: PropTypes.string.isRequired,
- icon: PropTypes.string.isRequired,
- iconAlt: PropTypes.message.isRequired,
- to: PropTypes.string.isRequired,
- }).isRequired,
- modifyAppContainerClasses: PropTypes.bool,
}
SideNavigation.defaultProps = {
- appContainerId: 'app',
- modifyAppContainerClasses: true,
className: undefined,
}
-const PortalledSideNavigation = props =>
- ReactDom.createPortal(
, document.getElementById('sidebar'))
-
-PortalledSideNavigation.Item = SideNavigationItem
+SideNavigation.Item = SideNavigationItem
-export { PortalledSideNavigation as default, SideNavigation }
+export default SideNavigation
diff --git a/pkg/webui/components/navigation/side/item/index.js b/pkg/webui/components/navigation/side/item/index.js
index 82b9bda801..b5ad39610a 100644
--- a/pkg/webui/components/navigation/side/item/index.js
+++ b/pkg/webui/components/navigation/side/item/index.js
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import React, { useCallback, useContext, useEffect, useState } from 'react'
+import React, { useCallback, useEffect, useState } from 'react'
import classnames from 'classnames'
import Dropdown from '@ttn-lw/components/dropdown'
+import MenuLink from '@ttn-lw/components/sidebar/side-menu-link'
+import Button from '@ttn-lw/components/button'
import Icon from '@ttn-lw/components/icon'
import Message from '@ttn-lw/lib/components/message'
@@ -23,8 +25,6 @@ import Message from '@ttn-lw/lib/components/message'
import PropTypes from '@ttn-lw/lib/prop-types'
import SideNavigationList from '../list'
-import NavigationLink from '../../link'
-import SideNavigationContext from '../context'
import style from './item.styl'
@@ -35,8 +35,7 @@ const handleItemClick = event => {
}
const SideNavigationItem = props => {
- const { className, children, title, depth, icon, path, exact, isActive } = props
- const { isMinimized, onLeafItemClick } = useContext(SideNavigationContext)
+ const { className, children, title, depth, icon, path, exact, isActive, isMinimized } = props
const [isExpanded, setIsExpanded] = useState(false)
const handleExpandCollapsableItem = useCallback(() => {
@@ -53,7 +52,7 @@ const SideNavigationItem = props => {
[],
)
for (const path of paths) {
- if (location.pathname.startsWith(path)) {
+ if (location.pathname.includes(path)) {
setIsExpanded(true)
return
}
@@ -62,11 +61,7 @@ const SideNavigationItem = props => {
}, [children])
return (
-
+
{Boolean(children) ? (
{
/>
) : (
{
const subItems = children
.filter(item => Boolean(item) && 'props' in item)
@@ -140,36 +136,54 @@ const CollapsableItem = ({
icon: item.props.icon,
}))
- const subItemActive = subItems.some(item => item.path === currentPathName)
+ const subItemActive = subItems.some(item => currentPathName.includes(item.path))
return (
<>
-
{icon && }
-
-
-
-
-
- {subItems.map(item => (
-
- ))}
-
-
- {children}
-
+ {!isMinimized && (
+ <>
+
+
+ >
+ )}
+ {isMinimized && (
+
+
+
+ {subItems.map(item => (
+
+ ))}
+
+
+ )}
+
+ {!isMinimized && (
+
+ {children}
+
+ )}
>
)
}
@@ -179,8 +193,8 @@ CollapsableItem.propTypes = {
currentPathName: PropTypes.string.isRequired,
depth: PropTypes.number.isRequired,
icon: PropTypes.string,
- isActive: PropTypes.bool.isRequired,
isExpanded: PropTypes.bool.isRequired,
+ isMinimized: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
onDropdownItemsClick: PropTypes.func,
title: PropTypes.message.isRequired,
@@ -192,8 +206,8 @@ CollapsableItem.defaultProps = {
onDropdownItemsClick: () => null,
}
-const LinkItem = ({ onClick, title, icon, exact, path, onDropdownItemsClick }) => {
- const handleLinkItemClick = React.useCallback(
+const LinkItem = ({ onClick, title, icon, exact, path }) => {
+ const handleLinkItemClick = useCallback(
event => {
document.activeElement.blur()
onClick(event)
@@ -203,19 +217,7 @@ const LinkItem = ({ onClick, title, icon, exact, path, onDropdownItemsClick }) =
return (
<>
-
- {icon && }
-
-
-
-
-
+
>
)
}
@@ -224,7 +226,6 @@ LinkItem.propTypes = {
exact: PropTypes.bool.isRequired,
icon: PropTypes.string,
onClick: PropTypes.func,
- onDropdownItemsClick: PropTypes.func,
path: PropTypes.string,
title: PropTypes.message.isRequired,
}
@@ -233,7 +234,6 @@ LinkItem.defaultProps = {
icon: undefined,
path: undefined,
onClick: () => null,
- onDropdownItemsClick: () => null,
}
export default SideNavigationItem
diff --git a/pkg/webui/components/navigation/side/item/item.styl b/pkg/webui/components/navigation/side/item/item.styl
index 4ecc7a7d2e..ae017616e8 100644
--- a/pkg/webui/components/navigation/side/item/item.styl
+++ b/pkg/webui/components/navigation/side/item/item.styl
@@ -13,106 +13,53 @@
// limitations under the License.
.item
- +media-query-min($bp.s)
- &-minimized
- position: relative
- .message,
- .expand-icon
- .sub-items
- display: none
-
- .icon
- margin-right: 0
-
- .link,
- .button
- display: flex
- padding: $cs.m 0
- height: auto
- justify-content: space-evenly
-
- &:focus-within,
- &:hover
- .fly-out-list
- display: block
+ &:hover
+ .fly-out-list
+ display: block
.button
reset-button()
- one-liner()
width: 100%
- display: block
- text-align: left
+ transition: 0.2s background-color ease-in, 0.2s color ease-in
+ text-decoration: none
+ display: flex
+ align-items: center
position: relative
- padding: $cs.s $cs.xxl $cs.s $cs.xl
- height: 3.1rem
- transition: background-color $ad.m, color $ad.s ease-in
+ gap: $cs.xs !important
- &:active
- background-color: lighter($c-active-blue-hover, 10)
+ span
+ &:not(:first-child)
+ color: $c.grey-700 !important
- &-active,
- &:hover
- color: $c-active-blue
+ &:hover&:not(&-active)
+ background: none !important
+ color: $tc-deep-gray !important
+ text-decoration: none
-.link
- one-liner()
- color: $tc-deep-gray
- padding: $cs.m $cs.xxl $cs.m $cs.xl
- display: block
- position: relative
+ span
+ &:not(:first-child)
+ color: $c.grey-900 !important
- &:not(&-active)::before // @stylint ignore
- content: ''
- position: absolute
- z-index: -1
- top: 0
- left: 0
- width: 0
- height: 100%
- background-color: $c-backdrop-lighter
- transition: width $ad.s ease-out 0s, background-color $ad.s linear
-
- &:hover:not(&-active)::before // @stylint ignore
- width: 100%
- transition: width $ad.s ease-out $ad.m, background-color $ad.s linear
-
- &::after
- content: ''
- position: absolute
- top: 0
- left: 0
- width: 3px
- height: 100%
- background-color: $c-subtle-fill
- transform: scaleY(0)
- transform-origin: center
- transition: transform $ad.m ease-in-out, background-color $ad.s linear
-
- &:hover:not(&-active)::after // @stylint ignore
- transform: scaleY(1)
+ .icon
+ color: inherit
&-active
- background-color: $c-backdrop-lighter
- color: $c-active-blue
-
- &::before
- content: ''
- position: absolute
- bottom: 0
- left: 0
- height: 100%
- display: block
- width: 3px
- background: $c-active-blue
+ background: $c.tts-primary-150
+ color: $c.grey-900 !important
- .icon
- color: $c-active-blue
+ &:hover
+ background: $c.tts-primary-150 !important
+ color: $c.grey-900 !important
-.icon
- margin-right: $cs.s
- vertical-align: middle
- color: $c-icon-fill
- font-size: 1.5rem
+ span
+ &:not(:first-child)
+ color: $c.grey-900 !important
+
+ .icon
+ color: inherit
+
+ .icon
+ color: inherit
.message
vertical-align: middle
@@ -127,16 +74,14 @@
&-open
transform: translateY(-50%) rotate(180deg)
-.fly-out-list
- display: none
- left: 3rem
+.fly-out-list-container
top: 0
+ left: 0
+ position: absolute
+ width: 180%
+ height: 13rem
- &::before
- content: ''
- position: absolute
- top: -10px
- bottom: -10px
- left: -10px
- right: -10px
- z-index: -1
+.fly-out-list
+ display: none
+ left: 5rem !important
+ top: 0 !important
diff --git a/pkg/webui/components/navigation/side/list/index.js b/pkg/webui/components/navigation/side/list/index.js
index 4640991c93..4561c58e4c 100644
--- a/pkg/webui/components/navigation/side/list/index.js
+++ b/pkg/webui/components/navigation/side/list/index.js
@@ -25,6 +25,7 @@ const SideNavigationList = ({ children, className, depth, isExpanded }) => {
[style.listNested]: !isRoot,
[style.listExpanded]: isExpanded,
})
+
return
}
diff --git a/pkg/webui/components/navigation/side/list/list.styl b/pkg/webui/components/navigation/side/list/list.styl
index 8b850437bb..baddbf7c23 100644
--- a/pkg/webui/components/navigation/side/list/list.styl
+++ b/pkg/webui/components/navigation/side/list/list.styl
@@ -16,7 +16,8 @@
list-style-type: none
padding: 0
margin: 0
- overflow: hidden
+ overflow: visible
+ font-size: $fs.m
& > li:not(:last-child)
margin-bottom: 0
@@ -28,18 +29,18 @@
transition: height $ad.m ease-in-out
&-nested > li
- height: 0
- transition: visibility $ad.m, height $ad.m ease-in-out
+ height: 0px
+ transition: height $ad.m ease-in-out
&-nested:not(.list-expanded) > li
visibility: hidden
&-expanded > li
- height: 3.1rem
+ height: 2.4rem
&-expanded, &-nested
& li a
- padding-left: $cs.l + $cs.l
+ padding-left: $cs.l + $cs.xs
.icon
margin-right: $cs.xs
diff --git a/pkg/webui/components/navigation/side/side.styl b/pkg/webui/components/navigation/side/side.styl
index 5d1e60a491..05670b0362 100644
--- a/pkg/webui/components/navigation/side/side.styl
+++ b/pkg/webui/components/navigation/side/side.styl
@@ -12,14 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-$minimize-button-height = 5rem
-
-// This style is to be applied to the portalled container.
.container
border-normal(right)
display: flex
flex-direction: column
- background-color: white
z-index: $zi.nav
+media-query-min($bp.s)
left: 0
@@ -34,50 +30,10 @@ $minimize-button-height = 5rem
+media-query-height(500px)
position: static
- .minimize-button
- reset-button()
- transition-color()
- border-normal(top)
- box-sizing: border-box
- color: $tc-subtle-gray
- background-color: white
- position: sticky
- top: "calc(100vh - %s)" % $minimize-button-height
- width: 100%
- height: $minimize-button-height
- margin-bottom: 1px
- flex-shrink: 0
- &:not(.minimize-button-minimized)
- padding-left: $cs.l
- text-align: left
- &-minimized
- padding-left: 0
- text-align: center
- +media-query-min($bp.s)
- display: block
- +media-query($bp.s)
- display: none
- &:hover
- color: $tc-deep-gray
- span:first-child
- nudge('up')
-
-
.navigation
- sidebar-transition(width)
- position: sticky
- top: 0
- background-color: white
box-sizing: border-box
max-height: 100%
- +media-query-min($bp.s)
- padding-bottom: $minimize-button-height
- width: $sidebar-width
- overflow-y: auto
- overflow-x: hidden
- +media-query($bp.s)
- height: $sidebar-mobile-menu-height
- width: 100%
+ width: 100%
&-minimized
overflow: visible
@@ -107,36 +63,6 @@ $minimize-button-height = 5rem
.drawer
overflow: visible
-.header
- border-normal(bottom)
- sidebar-transition(padding)
- height: 6rem
- color: $tc-deep-gray
- box-sizing: border-box
- display: flex
- align-items: center
- padding: $cs.xl $cs.l
- font-weight: $fw.bold
-
-.mobile-header
- align-items: center
- +media-query-min($bp.s)
- display: none
- +media-query($bp.s)
- border-normal(bottom)
- display: flex
- padding: 0 $cs.s
- height: $sidebar-mobile-menu-height
-
- .expand-icon
- margin-right: $cs.s
- font-size: 2rem
- color: $tc-subtle-gray
-
-.icon
- width: 2rem
- margin-right: $cs.m
-
.drawer
background-color: white
z-index: $zi.nav
diff --git a/pkg/webui/components/navigation/side/story.js b/pkg/webui/components/navigation/side/story.js
index c29c5b473f..baf5acf7cd 100644
--- a/pkg/webui/components/navigation/side/story.js
+++ b/pkg/webui/components/navigation/side/story.js
@@ -14,57 +14,44 @@
import React from 'react'
-import applicationIcon from '@assets/misc/application.svg'
+import SidebarContext from '@console/containers/side-bar/context'
import SideNavigationItem from './item'
-import { SideNavigation } from '.'
+import SideNavigation from '.'
export default {
- title: 'Navigation',
+ title: 'Navigation v2',
component: SideNavigation,
+ decorators: [
+ storyFn => (
+ {storyFn()}
+ ),
+ ],
}
-export const _SideNavigation = () => {
- const header = {
- title: 'test-application',
- icon: applicationIcon,
- iconAlt: { id: 'application', defaultMessage: 'Application' },
- to: '/',
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-_SideNavigation.story = {
- name: 'SideNavigation',
-}
+export const _SideNavigation = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/pkg/webui/components/profile-dropdown-v2/index.js b/pkg/webui/components/profile-dropdown-v2/index.js
deleted file mode 100644
index 0b557ea93f..0000000000
--- a/pkg/webui/components/profile-dropdown-v2/index.js
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React, { useCallback, useRef, useState } from 'react'
-import classnames from 'classnames'
-
-import Icon from '@ttn-lw/components/icon'
-import Dropdown from '@ttn-lw/components/dropdown-v2'
-import ProfilePicture from '@ttn-lw/components/profile-picture'
-import Button from '@ttn-lw/components/button-v2'
-import style from '@ttn-lw/components/button-v2/button.styl'
-
-import PropTypes from '@ttn-lw/lib/prop-types'
-
-import styles from './profile-dropdown-v2.styl'
-
-const ProfileDropdown = props => {
- const [expanded, setExpanded] = useState(false)
- const node = useRef(null)
- const { brandLogo, className, children, profilePicture, ...rest } = props
-
- const handleClickOutside = useCallback(e => {
- if (node.current && !node.current.contains(e.target)) {
- setExpanded(false)
- }
- }, [])
-
- const toggleDropdown = useCallback(() => {
- setExpanded(oldExpanded => {
- const newState = !oldExpanded
- if (newState) document.addEventListener('mousedown', handleClickOutside)
- else document.removeEventListener('mousedown', handleClickOutside)
- return newState
- })
- }, [handleClickOutside])
-
- return (
-
-
- {brandLogo &&
}
-
-
-
- {children}
-
- )
-}
-
-ProfileDropdown.propTypes = {
- brandLogo: PropTypes.shape({
- src: PropTypes.string.isRequired,
- alt: PropTypes.string.isRequired,
- }),
- /**
- * A list of items for the dropdown component. See ` `'s `items`
- * proptypes for details.
- */
- children: PropTypes.node.isRequired,
- className: PropTypes.string,
- /** The profile picture of the current user. */
- profilePicture: PropTypes.profilePicture,
-}
-
-ProfileDropdown.defaultProps = {
- brandLogo: undefined,
- className: undefined,
- profilePicture: undefined,
-}
-
-export default ProfileDropdown
diff --git a/pkg/webui/components/profile-dropdown-v2/profile-dropdown-v2.styl b/pkg/webui/components/profile-dropdown-v2/profile-dropdown-v2.styl
deleted file mode 100644
index e22504b7f5..0000000000
--- a/pkg/webui/components/profile-dropdown-v2/profile-dropdown-v2.styl
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-.container
- padding: 0 0.25rem 0 0.5rem
- position: relative
- gap: 0
-
-.brand-logo
- height: 1.14rem
-
- +media-query($bp.s)
- display: none
-
-.profile-picture
- height: 1.5rem !important
- width: 1.5rem !important
diff --git a/pkg/webui/components/profile-dropdown-v2/story.js b/pkg/webui/components/profile-dropdown-v2/story.js
deleted file mode 100644
index dfbef0d5e4..0000000000
--- a/pkg/webui/components/profile-dropdown-v2/story.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import React from 'react'
-
-import Dropdown from '@ttn-lw/components/dropdown-v2'
-import ExampleLogo from '@ttn-lw/components/logo/story-logo-new.svg'
-
-import ProfileDropdown from '.'
-
-const handleLogout = () => {
- // eslint-disable-next-line no-console
- console.log('Click')
-}
-
-export default {
- title: 'Profile Dropdown V2',
- component: ProfileDropdown,
-}
-
-export const Default = () => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)
diff --git a/pkg/webui/components/profile-dropdown/index.js b/pkg/webui/components/profile-dropdown/index.js
index 1c69eee1ea..6b9046b837 100644
--- a/pkg/webui/components/profile-dropdown/index.js
+++ b/pkg/webui/components/profile-dropdown/index.js
@@ -18,6 +18,8 @@ import classnames from 'classnames'
import Icon from '@ttn-lw/components/icon'
import Dropdown from '@ttn-lw/components/dropdown'
import ProfilePicture from '@ttn-lw/components/profile-picture'
+import Button from '@ttn-lw/components/button'
+import style from '@ttn-lw/components/button/button.styl'
import PropTypes from '@ttn-lw/lib/prop-types'
@@ -26,7 +28,7 @@ import styles from './profile-dropdown.styl'
const ProfileDropdown = props => {
const [expanded, setExpanded] = useState(false)
const node = useRef(null)
- const { userName, className, children, profilePicture, ...rest } = props
+ const { brandLogo, className, children, profilePicture, ...rest } = props
const handleClickOutside = useCallback(e => {
if (node.current && !node.current.contains(e.target)) {
@@ -44,23 +46,33 @@ const ProfileDropdown = props => {
}, [handleClickOutside])
return (
-
-
-
{userName}
-
- {expanded &&
{children} }
-
+
+ {brandLogo &&
}
+
+
+
+ {children}
+
)
}
ProfileDropdown.propTypes = {
+ brandLogo: PropTypes.shape({
+ src: PropTypes.string.isRequired,
+ alt: PropTypes.string.isRequired,
+ }),
/**
* A list of items for the dropdown component. See ` `'s `items`
* proptypes for details.
@@ -69,11 +81,10 @@ ProfileDropdown.propTypes = {
className: PropTypes.string,
/** The profile picture of the current user. */
profilePicture: PropTypes.profilePicture,
- /** The name/id of the current user. */
- userName: PropTypes.string.isRequired,
}
ProfileDropdown.defaultProps = {
+ brandLogo: undefined,
className: undefined,
profilePicture: undefined,
}
diff --git a/pkg/webui/components/profile-dropdown/profile-dropdown.styl b/pkg/webui/components/profile-dropdown/profile-dropdown.styl
index af14a2d1e5..e22504b7f5 100644
--- a/pkg/webui/components/profile-dropdown/profile-dropdown.styl
+++ b/pkg/webui/components/profile-dropdown/profile-dropdown.styl
@@ -1,4 +1,4 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -13,37 +13,16 @@
// limitations under the License.
.container
- display: inline-flex
- align-items: center
+ padding: 0 0.25rem 0 0.5rem
position: relative
- cursor: pointer
- transition: background-color $ad.s
- height: 100%
- padding: 0 $cs.xl
+ gap: 0
- &:hover
- area-hover()
+.brand-logo
+ height: 1.14rem
- +focus-visible()
- background-color: $c-backdrop-lighter
-
- &:active, &:focus
- area-active()
-
-.id
- one-liner()
- margin-right: $cs.xs
-
- +media-query($bp.l)
+ +media-query($bp.s)
display: none
-.dropdown
- top: $header-height
-
- &-icon
- margin-right: -.4rem
-
.profile-picture
- height: 3rem
- width: 3rem
- margin-right: $cs.m
+ height: 1.5rem !important
+ width: 1.5rem !important
diff --git a/pkg/webui/components/profile-dropdown/story.js b/pkg/webui/components/profile-dropdown/story.js
index 1d3c30be8e..a08ba25b67 100644
--- a/pkg/webui/components/profile-dropdown/story.js
+++ b/pkg/webui/components/profile-dropdown/story.js
@@ -1,4 +1,4 @@
-// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
+// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
import React from 'react'
import Dropdown from '@ttn-lw/components/dropdown'
+import ExampleLogo from '@ttn-lw/components/logo/story-logo-new.svg'
import ProfileDropdown from '.'
@@ -24,15 +25,27 @@ const handleLogout = () => {
}
export default {
- title: 'Profile Dropdown',
+ title: 'Profile Dropdown V2',
component: ProfileDropdown,
}
export const Default = () => (
-
-
-
-
+
)
diff --git a/pkg/webui/components/sidebar/dedicated-entity/index.js b/pkg/webui/components/sidebar/dedicated-entity/index.js
index 8bf68e8eb1..9255137997 100644
--- a/pkg/webui/components/sidebar/dedicated-entity/index.js
+++ b/pkg/webui/components/sidebar/dedicated-entity/index.js
@@ -16,7 +16,7 @@ import React from 'react'
import { NavLink } from 'react-router-dom'
import classnames from 'classnames'
-import Button from '@ttn-lw/components/button-v2'
+import Button from '@ttn-lw/components/button'
import Message from '@ttn-lw/lib/components/message'
diff --git a/pkg/webui/components/sidebar/search-button/index.js b/pkg/webui/components/sidebar/search-button/index.js
index 2d47a29ba3..601f622f51 100644
--- a/pkg/webui/components/sidebar/search-button/index.js
+++ b/pkg/webui/components/sidebar/search-button/index.js
@@ -15,7 +15,7 @@
import React, { useCallback, useContext } from 'react'
import classnames from 'classnames'
-import Button from '@ttn-lw/components/button-v2'
+import Button from '@ttn-lw/components/button'
import Icon from '@ttn-lw/components/icon'
import Message from '@ttn-lw/lib/components/message'
diff --git a/pkg/webui/components/sidebar/section-label/index.js b/pkg/webui/components/sidebar/section-label/index.js
index 88ee21981a..a1a9fcfae8 100644
--- a/pkg/webui/components/sidebar/section-label/index.js
+++ b/pkg/webui/components/sidebar/section-label/index.js
@@ -15,7 +15,7 @@
import React from 'react'
import classnames from 'classnames'
-import Button from '@ttn-lw/components/button-v2'
+import Button from '@ttn-lw/components/button'
import Message from '@ttn-lw/lib/components/message'
diff --git a/pkg/webui/components/sidebar/side-footer/index.js b/pkg/webui/components/sidebar/side-footer/index.js
index aa85a59682..381686fdca 100644
--- a/pkg/webui/components/sidebar/side-footer/index.js
+++ b/pkg/webui/components/sidebar/side-footer/index.js
@@ -15,8 +15,8 @@
import React, { useCallback, useContext, useRef } from 'react'
import classnames from 'classnames'
-import Button from '@ttn-lw/components/button-v2'
-import Dropdown from '@ttn-lw/components/dropdown-v2'
+import Button from '@ttn-lw/components/button'
+import Dropdown from '@ttn-lw/components/dropdown'
import { LanguageContext } from '@ttn-lw/lib/components/with-locale'
@@ -137,7 +137,7 @@ const SideFooter = () => {
message="EU1"
noDropdownIcon
dropdownItems={clusterDropdownItems}
- dropdownClassName={style.sideFooterDropdown}
+ dropdownClassName={classnames(style.sideFooterDropdown, style.sideFooterClusterDropdown)}
ref={clusterButtonRef}
/>
)}
diff --git a/pkg/webui/components/sidebar/side-footer/side-footer.styl b/pkg/webui/components/sidebar/side-footer/side-footer.styl
index 76f2da9c53..82d2d74b46 100644
--- a/pkg/webui/components/sidebar/side-footer/side-footer.styl
+++ b/pkg/webui/components/sidebar/side-footer/side-footer.styl
@@ -26,4 +26,7 @@
right: -79px !important
&-dropdown
- width: 18rem
+ width: 18.2rem
+
+ &-cluster-dropdown
+ left: -207px !important
diff --git a/pkg/webui/components/sidebar/side-header/index.js b/pkg/webui/components/sidebar/side-header/index.js
index d3885d1627..aa18230693 100644
--- a/pkg/webui/components/sidebar/side-header/index.js
+++ b/pkg/webui/components/sidebar/side-header/index.js
@@ -17,7 +17,7 @@ import classnames from 'classnames'
import LAYOUT from '@ttn-lw/constants/layout'
-import Button from '@ttn-lw/components/button-v2'
+import Button from '@ttn-lw/components/button'
import SidebarContext from '@console/containers/side-bar/context'
diff --git a/pkg/webui/console/containers/header/index.js b/pkg/webui/console/containers/header/index.js
index 8bbb7283fc..b26a07df9d 100644
--- a/pkg/webui/console/containers/header/index.js
+++ b/pkg/webui/console/containers/header/index.js
@@ -16,11 +16,10 @@ import React, { useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import HeaderComponent from '@ttn-lw/components/header'
-import NavigationBar from '@ttn-lw/components/navigation/bar'
import Dropdown from '@ttn-lw/components/dropdown'
-import PropTypes from '@ttn-lw/lib/prop-types'
import sharedMessages from '@ttn-lw/lib/shared-messages'
+import { selectAssetsRootPath, selectBrandingRootPath } from '@ttn-lw/lib/selectors/env'
import selectAccountUrl from '@console/lib/selectors/app-config'
import {
@@ -28,7 +27,6 @@ import {
mayViewApplications,
mayViewGateways,
mayViewOrganizationsOfUser,
- mayViewOrEditApiKeys,
} from '@console/lib/feature-checks'
import { logout } from '@console/store/actions/logout'
@@ -39,12 +37,11 @@ import Logo from '../logo'
const accountUrl = selectAccountUrl()
-const Header = ({ searchable, handleSearchRequest }) => {
+const Header = () => {
const dispatch = useDispatch()
const handleLogout = useCallback(() => dispatch(logout()), [dispatch])
const user = useSelector(selectUser)
- const isUserAdmin = useSelector(selectUserIsAdmin)
const mayViewApps = useSelector(state =>
user ? checkFromState(mayViewApplications, state) : false,
)
@@ -52,44 +49,28 @@ const Header = ({ searchable, handleSearchRequest }) => {
const mayViewOrgs = useSelector(state =>
user ? checkFromState(mayViewOrganizationsOfUser, state) : false,
)
- const mayHandleApiKeys = useSelector(state =>
- user ? checkFromState(mayViewOrEditApiKeys, state) : false,
- )
+ const isAdmin = useSelector(selectUserIsAdmin)
- const navigation = [
- {
- title: sharedMessages.overview,
- icon: 'overview',
- path: '',
- exact: true,
- hidden: !mayViewApps && !mayViewGateways,
- },
- {
- title: sharedMessages.applications,
- icon: 'application',
- path: '/applications',
- hidden: !mayViewApps,
- },
- {
- title: sharedMessages.gateways,
- icon: 'gateway',
- path: '/gateways',
- hidden: !mayViewGtws,
- },
- {
- title: sharedMessages.organizations,
- icon: 'organization',
- path: '/organizations',
- hidden: !mayViewOrgs,
- },
- ]
-
- const navigationEntries = (
-
- {navigation.map(
- ({ hidden, ...rest }) => !hidden && ,
+ const plusDropdownItems = (
+ <>
+ {mayViewApps && (
+
)}
-
+ {mayViewGtws && }
+ {mayViewOrgs && (
+
+ )}
+
+
+ >
)
const dropdownItems = (
@@ -100,54 +81,7 @@ const Header = ({ searchable, handleSearchRequest }) => {
path={`${accountUrl}/profile-settings`}
external
/>
- {mayHandleApiKeys && (
-
- )}
-
-
-
-
-
-
-
- )
-
- const mobileDropdownItems = (
-
- {navigation.map(
- ({ hidden, ...rest }) => !hidden && ,
- )}
-
-
-
-
- {mayHandleApiKeys && (
-
- )}
- {isUserAdmin && (
+ {isAdmin && (
{
path="https://thethingsindustries.com/docs"
external
/>
+
+
)
+ const hasCustomBranding = selectBrandingRootPath() !== selectAssetsRootPath()
+ const brandLogo = hasCustomBranding
+ ? {
+ src: `${selectBrandingRootPath()}/logo.svg`,
+ alt: 'Logo',
+ }
+ : undefined
+
return (
}
/>
)
}
-Header.propTypes = {
- /** A handler for when the user used the search input. */
- handleSearchRequest: PropTypes.func,
- /** A flag identifying whether the header should display the search input. */
- searchable: PropTypes.bool,
-}
-
-Header.defaultProps = {
- handleSearchRequest: () => null,
- searchable: false,
-}
-
export default Header
diff --git a/pkg/webui/console/containers/side-bar/navigation/app-list-side-navigation.js b/pkg/webui/console/containers/side-bar/navigation/app-list-side-navigation.js
index c2db6b7f41..00877d3b27 100644
--- a/pkg/webui/console/containers/side-bar/navigation/app-list-side-navigation.js
+++ b/pkg/webui/console/containers/side-bar/navigation/app-list-side-navigation.js
@@ -15,7 +15,7 @@
import React, { useContext } from 'react'
import SectionLabel from '@ttn-lw/components/sidebar/section-label'
-import SideNavigation from '@ttn-lw/components/navigation/side-v2'
+import SideNavigation from '@ttn-lw/components/navigation/side'
import sharedMessages from '@ttn-lw/lib/shared-messages'
diff --git a/pkg/webui/console/containers/side-bar/navigation/app-side-navigation.js b/pkg/webui/console/containers/side-bar/navigation/app-side-navigation.js
index 1a6cc78888..a5f4015354 100644
--- a/pkg/webui/console/containers/side-bar/navigation/app-side-navigation.js
+++ b/pkg/webui/console/containers/side-bar/navigation/app-side-navigation.js
@@ -16,7 +16,7 @@ import React, { useContext } from 'react'
import { useSelector } from 'react-redux'
import { defineMessages } from 'react-intl'
-import SideNavigation from '@ttn-lw/components/navigation/side-v2'
+import SideNavigation from '@ttn-lw/components/navigation/side'
import DedicatedEntity from '@ttn-lw/components/sidebar/dedicated-entity'
import sharedMessages from '@ttn-lw/lib/shared-messages'
diff --git a/pkg/webui/console/containers/side-bar/navigation/general-side-navigation.js b/pkg/webui/console/containers/side-bar/navigation/general-side-navigation.js
index aa10207fa7..4968245e95 100644
--- a/pkg/webui/console/containers/side-bar/navigation/general-side-navigation.js
+++ b/pkg/webui/console/containers/side-bar/navigation/general-side-navigation.js
@@ -15,7 +15,7 @@
import React, { useContext } from 'react'
import { useSelector } from 'react-redux'
-import SideNavigation from '@ttn-lw/components/navigation/side-v2'
+import SideNavigation from '@ttn-lw/components/navigation/side'
import SectionLabel from '@ttn-lw/components/sidebar/section-label'
import sharedMessages from '@ttn-lw/lib/shared-messages'
diff --git a/pkg/webui/console/containers/side-bar/navigation/gtw-list-side-navigation.js b/pkg/webui/console/containers/side-bar/navigation/gtw-list-side-navigation.js
index 39c300ce78..7442db0ceb 100644
--- a/pkg/webui/console/containers/side-bar/navigation/gtw-list-side-navigation.js
+++ b/pkg/webui/console/containers/side-bar/navigation/gtw-list-side-navigation.js
@@ -15,7 +15,7 @@
import React, { useContext } from 'react'
import SectionLabel from '@ttn-lw/components/sidebar/section-label'
-import SideNavigation from '@ttn-lw/components/navigation/side-v2'
+import SideNavigation from '@ttn-lw/components/navigation/side'
import sharedMessages from '@ttn-lw/lib/shared-messages'
diff --git a/pkg/webui/console/containers/side-bar/navigation/gtw-side-navigation.js b/pkg/webui/console/containers/side-bar/navigation/gtw-side-navigation.js
index 875ad38f2b..039f2b084c 100644
--- a/pkg/webui/console/containers/side-bar/navigation/gtw-side-navigation.js
+++ b/pkg/webui/console/containers/side-bar/navigation/gtw-side-navigation.js
@@ -16,7 +16,7 @@ import React, { useContext } from 'react'
import { useSelector } from 'react-redux'
import { defineMessages } from 'react-intl'
-import SideNavigation from '@ttn-lw/components/navigation/side-v2'
+import SideNavigation from '@ttn-lw/components/navigation/side'
import DedicatedEntity from '@ttn-lw/components/sidebar/dedicated-entity'
import sharedMessages from '@ttn-lw/lib/shared-messages'
diff --git a/pkg/webui/console/containers/side-bar/navigation/index.js b/pkg/webui/console/containers/side-bar/navigation/index.js
index 0adc2211c9..416b78dc01 100644
--- a/pkg/webui/console/containers/side-bar/navigation/index.js
+++ b/pkg/webui/console/containers/side-bar/navigation/index.js
@@ -27,14 +27,20 @@ const SidebarNavigation = () => {
const showGeneralSideNavigation =
!pathname.includes('/applications') && !pathname.includes('/gateways')
- const showAppSideNavigation = pathname.includes('/applications/')
+ const showSingleAppSideNavigation = pathname.match(
+ new RegExp('/applications/[a-z0-9](?:[-]?[a-z0-9]){1,}'),
+ )
+
+ const showSingleGatewaySideNavigation = pathname.match(
+ new RegExp('/gateways/[a-z0-9](?:[-]?[a-z0-9]){1,}'),
+ )
return (
{showGeneralSideNavigation &&
}
- {showAppSideNavigation &&
}
+ {showSingleAppSideNavigation &&
}
{pathname.includes('/applications') &&
}
- {pathname.includes('/gateways/') &&
}
+ {showSingleGatewaySideNavigation &&
}
{pathname.includes('/gateways') &&
}
)
diff --git a/pkg/webui/console/containers/side-bar/side-bar.styl b/pkg/webui/console/containers/side-bar/side-bar.styl
index 966d0ae22d..5fccfb397f 100644
--- a/pkg/webui/console/containers/side-bar/side-bar.styl
+++ b/pkg/webui/console/containers/side-bar/side-bar.styl
@@ -18,6 +18,7 @@
font-size: $fs.s
height: calc(100vh - 1.5rem)
min-width: 20rem
+ width: 20rem
sidebar-transition(min-width)
+media-query($bp.m)
sidebar-transition(left, box-shadow)
diff --git a/pkg/webui/console/containers/side-bar/story.js b/pkg/webui/console/containers/side-bar/story.js
index 908e55400d..7d64118f2f 100644
--- a/pkg/webui/console/containers/side-bar/story.js
+++ b/pkg/webui/console/containers/side-bar/story.js
@@ -18,7 +18,7 @@ import classnames from 'classnames'
import TtsLogo from '@assets/static/tts-logo.svg'
import Switcher from '@ttn-lw/components/sidebar/switcher'
-import SideNavigation from '@ttn-lw/components/navigation/side-v2'
+import SideNavigation from '@ttn-lw/components/navigation/side'
import SideHeader from '@ttn-lw/components/sidebar/side-header'
import SearchButton from '@ttn-lw/components/sidebar/search-button'
import SideFooter from '@ttn-lw/components/sidebar/side-footer'
diff --git a/pkg/webui/console/views/app/app.styl b/pkg/webui/console/views/app/app.styl
index 89f16356e1..48e4510b14 100644
--- a/pkg/webui/console/views/app/app.styl
+++ b/pkg/webui/console/views/app/app.styl
@@ -12,27 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-.main
- position: relative
- display: flex
- flex-direction: column
- +media-query-min($bp.s)
- min-height: "calc(100vh - %s - %s)" % ($header-height $footer-height)
- +media-query($bp.s)
- min-height: "calc(100vh - %s - %s)" % ($header-height-mobile $footer-height)
- +media-query($bp.xs)
- min-height: "calc(100vh - %s - %s)" % ($header-height-mobile $footer-height * 2)
-
-global-sidebar()
-
.content
position: relative
- flex: 1
display: flex
flex-direction: column
.stage
- flex: 1
&:not(.stage-flex)
padding-bottom: $ls.xxs
&-flex
diff --git a/pkg/webui/console/views/app/index.js b/pkg/webui/console/views/app/index.js
index f2d8c6819c..b140007f20 100644
--- a/pkg/webui/console/views/app/index.js
+++ b/pkg/webui/console/views/app/index.js
@@ -25,9 +25,6 @@ import {
import classnames from 'classnames'
import { ToastContainer } from '@ttn-lw/components/toast'
-import sidebarStyle from '@ttn-lw/components/navigation/side/side.styl'
-
-import Footer from '@ttn-lw/containers/footer'
import GenericNotFound from '@ttn-lw/lib/components/full-view-error/not-found'
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
@@ -37,6 +34,7 @@ import FullViewError, { FullViewErrorInner } from '@ttn-lw/lib/components/full-v
import Header from '@console/containers/header'
import LogBackInModal from '@console/containers/log-back-in-modal'
+import Sidebar from '@console/containers/side-bar'
import Overview from '@console/views/overview'
import Applications from '@console/views/applications'
@@ -125,33 +123,36 @@ const Layout = () => {
<>
-
+
>
diff --git a/pkg/webui/console/views/application/index.js b/pkg/webui/console/views/application/index.js
index 7e876d2a1e..e873db883d 100644
--- a/pkg/webui/console/views/application/index.js
+++ b/pkg/webui/console/views/application/index.js
@@ -16,12 +16,8 @@ import React, { useEffect } from 'react'
import { Routes, Route, useParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
-import applicationIcon from '@assets/misc/application.svg'
-
import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
-import SideNavigation from '@ttn-lw/components/navigation/side'
import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
-import Breadcrumbs from '@ttn-lw/components/breadcrumbs'
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
import RequireRequest from '@ttn-lw/lib/components/require-request'
@@ -41,22 +37,9 @@ import ApplicationIntegrationsMqtt from '@console/views/application-integrations
import ApplicationIntegrationsLoRaCloud from '@console/views/application-integrations-lora-cloud'
import Devices from '@console/views/devices'
-import sharedMessages from '@ttn-lw/lib/shared-messages'
import { selectApplicationSiteName } from '@ttn-lw/lib/selectors/env'
-import {
- mayViewApplicationInfo,
- mayViewApplicationEvents,
- maySetApplicationPayloadFormatters,
- mayViewApplicationDevices,
- mayCreateOrEditApplicationIntegrations,
- mayEditBasicApplicationInfo,
- mayViewOrEditApplicationApiKeys,
- mayViewOrEditApplicationCollaborators,
- mayViewOrEditApplicationPackages,
- mayAddPubSubIntegrations,
- mayViewApplications,
-} from '@console/lib/feature-checks'
+import { mayAddPubSubIntegrations, mayViewApplications } from '@console/lib/feature-checks'
import {
getApplication,
@@ -65,10 +48,7 @@ import {
} from '@console/store/actions/applications'
import { getAsConfiguration } from '@console/store/actions/application-server'
-import {
- selectApplicationRights,
- selectSelectedApplication,
-} from '@console/store/selectors/applications'
+import { selectSelectedApplication } from '@console/store/selectors/applications'
import {
selectMqttProviderDisabled,
selectNatsProviderDisabled,
@@ -102,7 +82,6 @@ const ApplicationInner = () => {
const { appId } = useParams()
const application = useSelector(selectSelectedApplication)
const name = application.name || appId
- const rights = useSelector(selectApplicationRights)
const siteName = selectApplicationSiteName()
const natsDisabled = useSelector(selectNatsProviderDisabled)
const mqttDisabled = useSelector(selectMqttProviderDisabled)
@@ -115,85 +94,7 @@ const ApplicationInner = () => {
return (
<>
-
-
- {mayViewApplicationInfo.check(rights) && (
-
- )}
- {mayViewApplicationDevices.check(rights) && (
-
- )}
- {mayViewApplicationEvents.check(rights) && (
-
- )}
- {maySetApplicationPayloadFormatters.check(rights) && (
-
-
-
-
- )}
- {mayCreateOrEditApplicationIntegrations.check(rights) && (
-
-
-
- {mayAddPubSubIntegrations.check(natsDisabled, mqttDisabled) && (
-
- )}
- {mayViewOrEditApplicationPackages.check(rights) && (
-
- )}
-
- )}
- {mayViewOrEditApplicationCollaborators.check(rights) && (
-
- )}
- {mayViewOrEditApplicationApiKeys.check(rights) && (
-
- )}
- {mayEditBasicApplicationInfo.check(rights) && (
-
- )}
-
diff --git a/pkg/webui/console/views/applications-list/index.js b/pkg/webui/console/views/applications-list/index.js
index d0a6204937..c1b22146b6 100644
--- a/pkg/webui/console/views/applications-list/index.js
+++ b/pkg/webui/console/views/applications-list/index.js
@@ -15,21 +15,30 @@
import React from 'react'
import { Row, Col, Container } from 'react-grid-system'
+import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
+import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
+
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
import ApplicationsTable from '@console/containers/applications-table'
import sharedMessages from '@ttn-lw/lib/shared-messages'
-const ApplicationsList = () => (
-
-
-
-
-
-
-
-
-)
+const ApplicationsList = () => {
+ useBreadcrumbs('apps.list', )
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ )
+}
export default ApplicationsList
diff --git a/pkg/webui/console/views/applications/index.js b/pkg/webui/console/views/applications/index.js
index a22737072a..d05c6bb22c 100644
--- a/pkg/webui/console/views/applications/index.js
+++ b/pkg/webui/console/views/applications/index.js
@@ -16,6 +16,7 @@ import React from 'react'
import { Routes, Route } from 'react-router-dom'
import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
+import Breadcrumbs from '@ttn-lw/components/breadcrumbs'
import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
import GenericNotFound from '@ttn-lw/lib/components/full-view-error/not-found'
@@ -37,6 +38,7 @@ const Applications = () => {
return (
+
diff --git a/pkg/webui/console/views/gateway/index.js b/pkg/webui/console/views/gateway/index.js
index 535e25d252..9e9eb5139a 100644
--- a/pkg/webui/console/views/gateway/index.js
+++ b/pkg/webui/console/views/gateway/index.js
@@ -16,12 +16,8 @@ import React, { useCallback, useEffect } from 'react'
import { Routes, Route, useParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
-import gatewayIcon from '@assets/misc/gateway.svg'
-
import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
-import Breadcrumbs from '@ttn-lw/components/breadcrumbs'
-import SideNavigation from '@ttn-lw/components/navigation/side'
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
import GenericNotFound from '@ttn-lw/lib/components/full-view-error/not-found'
@@ -35,25 +31,15 @@ import GatewayApiKeys from '@console/views/gateway-api-keys'
import GatewayOverview from '@console/views/gateway-overview'
import attachPromise from '@ttn-lw/lib/store/actions/attach-promise'
-import sharedMessages from '@ttn-lw/lib/shared-messages'
import { selectApplicationSiteName } from '@ttn-lw/lib/selectors/env'
-import {
- mayViewGatewayInfo,
- mayViewGatewayEvents,
- mayViewOrEditGatewayLocation,
- mayViewOrEditGatewayCollaborators,
- mayViewOrEditGatewayApiKeys,
- mayEditBasicGatewayInformation,
-} from '@console/lib/feature-checks'
-
import {
getGateway,
stopGatewayEventsStream,
getGatewaysRightsList,
} from '@console/store/actions/gateways'
-import { selectSelectedGateway, selectGatewayRights } from '@console/store/selectors/gateways'
+import { selectSelectedGateway } from '@console/store/selectors/gateways'
const Gateway = () => {
const { gtwId } = useParams()
@@ -104,7 +90,6 @@ const Gateway = () => {
const GatewayInner = () => {
const { gtwId } = useParams()
const gateway = useSelector(selectSelectedGateway)
- const rights = useSelector(selectGatewayRights)
const gatewayName = gateway?.name || gtwId
@@ -112,43 +97,7 @@ const GatewayInner = () => {
return (
<>
-
-
- {mayViewGatewayInfo.check(rights) && (
-
- )}
- {mayViewGatewayEvents.check(rights) && (
-
- )}
- {mayViewOrEditGatewayLocation.check(rights) && (
-
- )}
- {mayViewOrEditGatewayCollaborators.check(rights) && (
-
- )}
- {mayViewOrEditGatewayApiKeys.check(rights) && (
-
- )}
- {mayEditBasicGatewayInformation.check(rights) && (
-
- )}
-
diff --git a/pkg/webui/console/views/gateways-list/index.js b/pkg/webui/console/views/gateways-list/index.js
index 1f8c7e10ef..3663d16265 100644
--- a/pkg/webui/console/views/gateways-list/index.js
+++ b/pkg/webui/console/views/gateways-list/index.js
@@ -15,21 +15,28 @@
import React from 'react'
import { Container, Row, Col } from 'react-grid-system'
+import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
+import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
+
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
import GatewaysTable from '@console/containers/gateways-table'
import sharedMessages from '@ttn-lw/lib/shared-messages'
-const GatewaysList = () => (
-
-
-
-
-
-
-
-
-)
+const GatewaysList = () => {
+ useBreadcrumbs('gtws.list', )
+
+ return (
+
+
+
+
+
+
+
+
+ )
+}
export default GatewaysList
diff --git a/pkg/webui/console/views/gateways/index.js b/pkg/webui/console/views/gateways/index.js
index 64878d4a8e..0e79168e57 100644
--- a/pkg/webui/console/views/gateways/index.js
+++ b/pkg/webui/console/views/gateways/index.js
@@ -17,6 +17,7 @@ import { Routes, Route } from 'react-router-dom'
import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
+import Breadcrumbs from '@ttn-lw/components/breadcrumbs'
import ValidateRouteParam from '@ttn-lw/lib/components/validate-route-param'
@@ -35,6 +36,7 @@ const Gateways = () => {
useBreadcrumbs('gtws', )
return (
+
diff --git a/pkg/webui/console/views/organization/index.js b/pkg/webui/console/views/organization/index.js
index 3ae3b0a883..5b055bcea4 100644
--- a/pkg/webui/console/views/organization/index.js
+++ b/pkg/webui/console/views/organization/index.js
@@ -16,12 +16,8 @@ import React, { useEffect } from 'react'
import { Routes, Route, useParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
-import organizationIcon from '@assets/misc/organization.svg'
-
import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
-import Breadcrumbs from '@ttn-lw/components/breadcrumbs'
-import SideNavigation from '@ttn-lw/components/navigation/side'
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
import GenericNotFound from '@ttn-lw/lib/components/full-view-error/not-found'
@@ -35,17 +31,9 @@ import OrganizationGeneralSettings from '@console/views/organization-general-set
import OrganizationApiKeys from '@console/views/organization-api-keys'
import OrganizationCollaborators from '@console/views/organization-collaborators'
-import sharedMessages from '@ttn-lw/lib/shared-messages'
import { selectApplicationSiteName } from '@ttn-lw/lib/selectors/env'
-import {
- checkFromState,
- mayViewOrganizationInformation,
- mayViewOrEditOrganizationApiKeys,
- mayViewOrEditOrganizationCollaborators,
- mayEditBasicOrganizationInformation,
- mayViewOrganizationsOfUser,
-} from '@console/lib/feature-checks'
+import { mayViewOrganizationsOfUser } from '@console/lib/feature-checks'
import {
getOrganization,
@@ -82,15 +70,6 @@ const OrganizationInner = () => {
const name = organization.name || orgId
const dispatch = useDispatch()
const siteName = selectApplicationSiteName()
- const mayViewOrEditApiKeys = useSelector(state =>
- checkFromState(mayViewOrEditOrganizationApiKeys, state),
- )
- const mayViewOrEditCollaborators = useSelector(state =>
- checkFromState(mayViewOrEditOrganizationCollaborators, state),
- )
- const mayEditInformation = useSelector(state =>
- checkFromState(mayEditBasicOrganizationInformation, state),
- )
useBreadcrumbs('orgs.single', )
@@ -103,38 +82,7 @@ const OrganizationInner = () => {
return (
-
-
- {mayViewOrganizationInformation && (
-
- )}
-
- {mayViewOrEditCollaborators && (
-
- )}
- {mayViewOrEditApiKeys && (
-
- )}
- {mayEditInformation && (
-
- )}
-
diff --git a/pkg/webui/console/views/organizations-list/index.js b/pkg/webui/console/views/organizations-list/index.js
index e0a9581f0c..a19ccdfc80 100644
--- a/pkg/webui/console/views/organizations-list/index.js
+++ b/pkg/webui/console/views/organizations-list/index.js
@@ -17,21 +17,28 @@ import { Row, Col, Container } from 'react-grid-system'
import PAGE_SIZES from '@ttn-lw/constants/page-sizes'
+import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
+import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
+
import IntlHelmet from '@ttn-lw/lib/components/intl-helmet'
import OrganizationsTable from '@console/containers/organizations-table'
import sharedMessages from '@ttn-lw/lib/shared-messages'
-const List = () => (
-
-
-
-
-
-
-
-
-)
+const List = () => {
+ useBreadcrumbs('orgs.list', )
+
+ return (
+
+
+
+
+
+
+
+
+ )
+}
export default List
diff --git a/pkg/webui/console/views/organizations/index.js b/pkg/webui/console/views/organizations/index.js
index 8dc902cc56..50982adfa3 100644
--- a/pkg/webui/console/views/organizations/index.js
+++ b/pkg/webui/console/views/organizations/index.js
@@ -17,6 +17,7 @@ import { Routes, Route } from 'react-router-dom'
import Breadcrumb from '@ttn-lw/components/breadcrumbs/breadcrumb'
import { useBreadcrumbs } from '@ttn-lw/components/breadcrumbs/context'
+import Breadcrumbs from '@ttn-lw/components/breadcrumbs'
import GenericNotFound from '@ttn-lw/lib/components/full-view-error/not-found'
import ValidateRouteParam from '@ttn-lw/lib/components/validate-route-param'
@@ -40,6 +41,7 @@ const Organizations = () => {
return (
+
diff --git a/pkg/webui/console/views/overview/index.js b/pkg/webui/console/views/overview/index.js
index 91e4a59ab7..46fb48e733 100644
--- a/pkg/webui/console/views/overview/index.js
+++ b/pkg/webui/console/views/overview/index.js
@@ -180,7 +180,6 @@ const Overview = () => {
- {chooser}
diff --git a/pkg/webui/lib/shared-messages.js b/pkg/webui/lib/shared-messages.js
index 738e3cf637..58b2fb48da 100644
--- a/pkg/webui/lib/shared-messages.js
+++ b/pkg/webui/lib/shared-messages.js
@@ -307,6 +307,7 @@ export default defineMessages({
'The Authentication Key for Lora Basics Station LNS connections. This field is ignored for other gateways.',
link: 'Link',
linked: 'Linked',
+ list: 'List',
liveData: 'Live data',
location: 'Location',
locationDescription:
diff --git a/pkg/webui/locales/en.json b/pkg/webui/locales/en.json
index 855eaf765b..405156007f 100644
--- a/pkg/webui/locales/en.json
+++ b/pkg/webui/locales/en.json
@@ -1246,6 +1246,7 @@
"lib.shared-messages.lbsLNSSecretDescription": "The Authentication Key for Lora Basics Station LNS connections. This field is ignored for other gateways.",
"lib.shared-messages.link": "Link",
"lib.shared-messages.linked": "Linked",
+ "lib.shared-messages.list": "List",
"lib.shared-messages.liveData": "Live data",
"lib.shared-messages.location": "Location",
"lib.shared-messages.locationDescription": "When set to public, the gateway location may be visible to other users of the network",
diff --git a/pkg/webui/locales/ja.json b/pkg/webui/locales/ja.json
index 402aa056e5..4f21652658 100644
--- a/pkg/webui/locales/ja.json
+++ b/pkg/webui/locales/ja.json
@@ -1246,6 +1246,7 @@
"lib.shared-messages.lbsLNSSecretDescription": "LoRa Basics Station LNS 接続のための認証キーです。このフィールドは、他のゲートウェイでは無視されます",
"lib.shared-messages.link": "リンク",
"lib.shared-messages.linked": "リンク済",
+ "lib.shared-messages.list": "",
"lib.shared-messages.liveData": "ライブデータ",
"lib.shared-messages.location": "場所",
"lib.shared-messages.locationDescription": "公開に設定すると、ゲートウェイの位置がネットワークの他のユーザーから見えることがあります",