Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Console search #7082

Merged
merged 16 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import backendMessages from '@ttn-lw/locales/.backend/en.json'
import '../../pkg/webui/styles/main.styl'
import '../../pkg/webui/styles/utilities/general.styl'
import '../../pkg/webui/styles/utilities/spacing.styl'
import '../../pkg/webui/styles/utilities/tokens.styl'
import 'focus-visible/dist/focus-visible'
import createStore from './store'
import Center from './center'
Expand Down
6 changes: 3 additions & 3 deletions pkg/webui/components/icon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ const Icon = forwardRef((props, ref) => {
const classname = classnames(className, {
[style.nudgeUp]: nudgeUp,
[style.nudgeDown]: nudgeDown,
[style.large]: large,
[style.small]: small,
[style.textPaddedLeft]: textPaddedLeft,
[style.textPaddedRight]: textPaddedRight,
})

return <ActualIcon className={classname} ref={ref} size={small ? 16 : size} {...rest} />
const renderedSize = large ? 24 : small ? 16 : size

return <ActualIcon className={classname} ref={ref} size={renderedSize} {...rest} />
})

Icon.propTypes = {
Expand Down
4 changes: 4 additions & 0 deletions pkg/webui/components/link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const Link = props => {
target,
showVisited,
onClick,
onMouseEnter,
secondary,
primary,
tabIndex,
Expand Down Expand Up @@ -88,6 +89,7 @@ const Link = props => {
to={to}
target={target}
onClick={onClick}
onMouseEnter={onMouseEnter}
tabIndex={tabIndex}
role={role}
>
Expand All @@ -102,6 +104,7 @@ Link.propTypes = {
disabled: PropTypes.bool,
id: PropTypes.string,
onClick: PropTypes.func,
onMouseEnter: PropTypes.func,
primary: PropTypes.bool,
replace: PropTypes.bool,
role: PropTypes.string,
Expand All @@ -128,6 +131,7 @@ Link.defaultProps = {
disabled: false,
id: undefined,
onClick: () => null,
onMouseEnter: undefined,
primary: false,
showVisited: false,
replace: false,
Expand Down
2 changes: 1 addition & 1 deletion pkg/webui/components/overlay/overlay.styl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
top: 0
left: 0
background: var(--c-bg-neutral-min)
transition: opacity $ad.m ease-in-out, visibility $ad.m ease-in-out
transition: opacity $ad.m ease-in-out $ad.l, visibility $ad.m ease-in-out

&-visible
visibility: visible
Expand Down
100 changes: 100 additions & 0 deletions pkg/webui/components/scroll-fader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright © 2024 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 } from 'react'

import PropTypes from '@ttn-lw/lib/prop-types'
import combineRefs from '@ttn-lw/lib/combine-refs'

import style from './scroll-fader.styl'

// ScrollFader is a component that fades out the content of a container when it
// is scrolled. It is used for scrollable elements that need some visual
// indication that they are scrollable, but do not have a scrollbar.
// The indication only shows when the content is scrolled.

const ScrollFader = React.forwardRef(({ children, className, fadeHeight }, ref) => {
const internalRef = useRef()
const combinedRef = combineRefs([ref, internalRef])

const handleScroll = useCallback(() => {
const container = internalRef.current
const { scrollTop, scrollHeight, clientHeight } = container
const scrollable = scrollHeight - clientHeight
const scrollGradientTop = container.querySelector(`.${style.scrollGradientTop}`)
const scrollGradientBottom = container.querySelector(`.${style.scrollGradientBottom}`)

if (scrollGradientTop) {
const opacity = scrollTop < fadeHeight ? scrollTop / fadeHeight : 1
scrollGradientTop.style.opacity = opacity
}

if (scrollGradientBottom) {
const scrollEnd = scrollable - fadeHeight
const opacity = scrollTop < scrollEnd ? 1 : (scrollable - scrollTop) / fadeHeight
scrollGradientBottom.style.opacity = opacity
}
}, [fadeHeight])

useEffect(() => {
const container = internalRef.current
if (!container) return

const mutationObserver = new MutationObserver(() => {
handleScroll()
})

// Run the calculation whenever the children change.
mutationObserver.observe(container, { attributes: false, childList: true, subtree: false })

handleScroll() // Call once on mount if needed
container.addEventListener('scroll', handleScroll)
window.addEventListener('resize', handleScroll)

return () => {
// Cleanup observer and event listeners
mutationObserver.disconnect()
container.removeEventListener('scroll', handleScroll)
window.removeEventListener('resize', handleScroll)
}
}, [handleScroll])

return (
<div
ref={combinedRef}
onScroll={handleScroll}
className={className}
style={{
position: 'relative',
}}
>
<div className={style.scrollGradientTop} />
{children}
<div className={style.scrollGradientBottom} />
</div>
)
})

ScrollFader.propTypes = {
children: PropTypes.node.isRequired,
className: PropTypes.string,
fadeHeight: PropTypes.number,
}

ScrollFader.defaultProps = {
className: undefined,
fadeHeight: 40,
}

export default ScrollFader
37 changes: 37 additions & 0 deletions pkg/webui/components/scroll-fader/scroll-fader.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright © 2024 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.

$scroll-gradient-height = 1rem

// Add a gradient to the bottom of the sidebar
// to signify that there is more content below.
.scroll-gradient-top, .scroll-gradient-bottom
position: sticky
left: 0
right: 0
height: $scroll-gradient-height
z-index: $zi.slight
opacity: 0
pointer-events: none

.scroll-gradient-top
top: 0
margin-bottom: - $scroll-gradient-height
background: linear-gradient(0deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, .05))

.scroll-gradient-bottom
bottom: 0
margin-top: - $scroll-gradient-height
background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, .05))

Loading
Loading