style(allocations): improve display of unset priorities
This commit is contained in:
parent
ee2e504ffa
commit
5090cca98b
@ -358,6 +358,10 @@ input[type="button"].btn-info:hover,
|
|||||||
.table__td--overriden
|
.table__td--overriden
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
|
|
||||||
|
.table__td--center > .table__td-content
|
||||||
|
margin: auto
|
||||||
|
width: max-content
|
||||||
|
|
||||||
.table__th
|
.table__th
|
||||||
background-color: var(--color-dark)
|
background-color: var(--color-dark)
|
||||||
position: relative
|
position: relative
|
||||||
@ -531,10 +535,9 @@ section
|
|||||||
.notification
|
.notification
|
||||||
position: relative
|
position: relative
|
||||||
border-radius: 3px
|
border-radius: 3px
|
||||||
padding: 10px 20px 20px
|
padding: 10px 20px 10px 100px
|
||||||
margin: 40px auto
|
margin: 40px auto
|
||||||
box-shadow: 0 0 4px 2px inset currentColor
|
box-shadow: 0 0 4px 2px inset currentColor
|
||||||
padding-left: 100px
|
|
||||||
min-height: 100px
|
min-height: 100px
|
||||||
max-width: 700px
|
max-width: 700px
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
transition: transform .2s ease
|
transition: transform .2s ease
|
||||||
transform: scaleY(0)
|
transform: scaleY(0)
|
||||||
transform-origin: top
|
transform-origin: top
|
||||||
|
z-index: 1
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color: var(--color-grey-light)
|
background-color: var(--color-grey-light)
|
||||||
|
|||||||
@ -5,12 +5,15 @@ import { MovementObserver } from '../../lib/movement-observer/movement-observer'
|
|||||||
var TOOLTIP_CLASS = 'tooltip';
|
var TOOLTIP_CLASS = 'tooltip';
|
||||||
var TOOLTIP_INITIALIZED_CLASS = 'tooltip--initialized';
|
var TOOLTIP_INITIALIZED_CLASS = 'tooltip--initialized';
|
||||||
var TOOLTIP_OPEN_CLASS = 'tooltip--active';
|
var TOOLTIP_OPEN_CLASS = 'tooltip--active';
|
||||||
|
var TOOLTIP_ORIENTATION_MARGIN = 10;
|
||||||
|
var TOOLTIP_CENTER_THRESHOLD = 20;
|
||||||
|
|
||||||
@Utility({
|
@Utility({
|
||||||
selector: `.${TOOLTIP_CLASS}`,
|
selector: `.${TOOLTIP_CLASS}`,
|
||||||
})
|
})
|
||||||
export class Tooltip {
|
export class Tooltip {
|
||||||
_element;
|
_element;
|
||||||
|
_handle;
|
||||||
_content;
|
_content;
|
||||||
|
|
||||||
_movementObserver;
|
_movementObserver;
|
||||||
@ -19,6 +22,8 @@ export class Tooltip {
|
|||||||
|
|
||||||
_xOffset = 3;
|
_xOffset = 3;
|
||||||
_yOffset = -10;
|
_yOffset = -10;
|
||||||
|
|
||||||
|
_xArrowOffset = 18;
|
||||||
|
|
||||||
constructor(element) {
|
constructor(element) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
@ -38,8 +43,9 @@ export class Tooltip {
|
|||||||
this._content = content;
|
this._content = content;
|
||||||
|
|
||||||
this._element = element;
|
this._element = element;
|
||||||
|
this._handle = element.querySelector('.tooltip__handle') || element;
|
||||||
|
|
||||||
this._movementObserver = new MovementObserver(this._element, { leadingCallback: this.close.bind(this) });
|
this._movementObserver = new MovementObserver(this._handle, { leadingCallback: this.close.bind(this) });
|
||||||
|
|
||||||
element.classList.add(TOOLTIP_INITIALIZED_CLASS);
|
element.classList.add(TOOLTIP_INITIALIZED_CLASS);
|
||||||
}
|
}
|
||||||
@ -103,9 +109,9 @@ export class Tooltip {
|
|||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_reposition() {
|
_reposition(forceRight, forceBottom) {
|
||||||
const doRight = this._decideRight();
|
const doRight = forceRight === undefined ? this._decideRight() : !!forceRight;
|
||||||
const doBottom = this._decideBottom();
|
const doBottom = forceBottom === undefined ? this._decideBottom() : !!forceBottom;
|
||||||
|
|
||||||
// console.log('doRight', doRight);
|
// console.log('doRight', doRight);
|
||||||
// console.log('doBottom', doBottom);
|
// console.log('doBottom', doBottom);
|
||||||
@ -120,34 +126,39 @@ export class Tooltip {
|
|||||||
else
|
else
|
||||||
this._element.classList.remove('tooltip--right');
|
this._element.classList.remove('tooltip--right');
|
||||||
|
|
||||||
const handleCoords = this._element.getBoundingClientRect();
|
const handleCoords = this._handle.getBoundingClientRect();
|
||||||
|
|
||||||
const left = doRight ? handleCoords.right - this._xOffset : handleCoords.left + this._xOffset;
|
for (let _i of [1,2]) { // eslint-disable-line no-unused-vars
|
||||||
const bottom = doBottom ? handleCoords.bottom - this._yOffset : handleCoords.top + this._yOffset;
|
const left = doRight ? handleCoords.right - this._getXOffset() - this._content.offsetWidth : handleCoords.left + this._getXOffset();
|
||||||
|
const top = doBottom ? handleCoords.bottom - this._yOffset : handleCoords.top + this._yOffset - this._content.offsetHeight;
|
||||||
|
|
||||||
if (doRight) {
|
|
||||||
const right = left - this._content.offsetWidth;
|
|
||||||
this._content.style.left = `${right}px`;
|
|
||||||
} else
|
|
||||||
this._content.style.left = `${left}px`;
|
this._content.style.left = `${left}px`;
|
||||||
|
|
||||||
if (doBottom)
|
|
||||||
this._content.style.top = `${bottom}px`;
|
|
||||||
else {
|
|
||||||
const top = bottom - this._content.offsetHeight;
|
|
||||||
this._content.style.top = `${top}px`;
|
this._content.style.top = `${top}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getXOffset() {
|
||||||
|
if (this._decideCenter())
|
||||||
|
return this._handle.offsetWidth / 2 - this._xArrowOffset;
|
||||||
|
else
|
||||||
|
return this._xOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
_decideCenter() {
|
||||||
|
return this._handle.offsetWidth <= TOOLTIP_CENTER_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
_decideBottom() {
|
_decideBottom() {
|
||||||
const handleCoords = this._element.getBoundingClientRect();
|
const handleCoords = this._handle.getBoundingClientRect();
|
||||||
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
|
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
|
||||||
const bottom = handleCoords.top + this._yOffset;
|
this._reposition(false, false);
|
||||||
const bottomBottom = handleCoords.bottom - this._yOffset;
|
const top = handleCoords.top + this._yOffset - this._content.offsetHeight - 2 * TOOLTIP_ORIENTATION_MARGIN;
|
||||||
|
const isHeight = Math.min(this._content.offsetHeight + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowHeight - top));
|
||||||
|
|
||||||
const isHeight = Math.min(windowHeight, bottom) - Math.max(0, bottom - this._content.offsetHeight);
|
this._reposition(false, true);
|
||||||
const isHeightBottom = Math.min(windowHeight, bottomBottom) - Math.max(0, bottomBottom - this._content.offsetHeight);
|
const topBottom = handleCoords.bottom - this._yOffset;
|
||||||
|
const isHeightBottom = Math.min(this._content.offsetHeight + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowHeight - topBottom));
|
||||||
|
|
||||||
// console.log('_decideBottom', isHeight, isHeightBottom);
|
// console.log('_decideBottom', isHeight, isHeightBottom);
|
||||||
|
|
||||||
@ -155,14 +166,16 @@ export class Tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_decideRight() {
|
_decideRight() {
|
||||||
const handleCoords = this._element.getBoundingClientRect();
|
const handleCoords = this._handle.getBoundingClientRect();
|
||||||
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
|
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
|
||||||
|
|
||||||
|
this._reposition(false, false);
|
||||||
|
const left = handleCoords.left + this._getXOffset();
|
||||||
|
const isWidth = Math.min(this._content.offsetWidth + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowWidth - left));
|
||||||
|
|
||||||
const left = handleCoords.left + this._yOffset;
|
this._reposition(true, false);
|
||||||
const leftRight = handleCoords.right - this._yOffset;
|
const leftRight = handleCoords.right - this._getXOffset() - this._content.offsetWidth - 2 * TOOLTIP_ORIENTATION_MARGIN;
|
||||||
|
const isWidthRight = Math.min(this._content.offsetWidth + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowWidth - leftRight));
|
||||||
const isWidth = Math.max(0, Math.min(windowWidth - left , this._content.offsetWidth)) + Math.min(0, left);
|
|
||||||
const isWidthRight = Math.max(0, Math.min(windowWidth - leftRight, this._content.offsetWidth)) + Math.min(0, leftRight);
|
|
||||||
|
|
||||||
// console.log('_decideRight', isWidth, isWidthRight);
|
// console.log('_decideRight', isWidth, isWidthRight);
|
||||||
|
|
||||||
|
|||||||
@ -103,15 +103,11 @@ instance CsvColumnsExplained AllocationUserTableCsv where
|
|||||||
getAUsersR, postAUsersR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html
|
getAUsersR, postAUsersR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html
|
||||||
getAUsersR = postAUsersR
|
getAUsersR = postAUsersR
|
||||||
postAUsersR tid ssh ash = do
|
postAUsersR tid ssh ash = do
|
||||||
(usersTable, missingPriorities) <- runDB $ do
|
usersTable <- runDB $ do
|
||||||
Entity aId _ <- getBy404 $ TermSchoolAllocationShort tid ssh ash
|
Entity aId _ <- getBy404 $ TermSchoolAllocationShort tid ssh ash
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
resultsDone <- (<= NTop (Just now)) . NTop <$> allocationDone aId
|
resultsDone <- (<= NTop (Just now)) . NTop <$> allocationDone aId
|
||||||
|
|
||||||
missingPriorities <- E.selectExists . E.from $ \allocationUser ->
|
|
||||||
E.where_ $ allocationUser E.^. AllocationUserAllocation E.==. E.val aId
|
|
||||||
E.&&. E.isNothing (allocationUser E.^. AllocationUserPriority)
|
|
||||||
|
|
||||||
csvName <- getMessageRender <*> pure (MsgAllocationUsersCsvName tid ssh ash)
|
csvName <- getMessageRender <*> pure (MsgAllocationUsersCsvName tid ssh ash)
|
||||||
|
|
||||||
let
|
let
|
||||||
@ -147,9 +143,11 @@ postAUsersR tid ssh ash = do
|
|||||||
, coursesModalApplied $ colAllocationApplied resultAppliedCourses
|
, coursesModalApplied $ colAllocationApplied resultAppliedCourses
|
||||||
, coursesModalVetoed $ colAllocationVetoed resultVetoedCourses
|
, coursesModalVetoed $ colAllocationVetoed resultVetoedCourses
|
||||||
, coursesModalAssigned . assignedHeated $ colAllocationAssigned resultAssignedCourses
|
, coursesModalAssigned . assignedHeated $ colAllocationAssigned resultAssignedCourses
|
||||||
, emptyOpticColonnade (resultAllocationUser . _entityVal . _allocationUserPriority . _Just) colAllocationPriority
|
, emptyOpticColonnade' emptyPriorityCell (resultAllocationUser . _entityVal . _allocationUserPriority . _Just) colAllocationPriority
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
|
emptyPriorityCell = addCellClass ("table__td--center" :: Text) . cell $
|
||||||
|
messageTooltip =<< messageIconI Error IconMissingAllocationPriority MsgAllocationMissingPrioritiesIgnored
|
||||||
assignedHeated
|
assignedHeated
|
||||||
| resultsDone = imapColonnade assignedHeated'
|
| resultsDone = imapColonnade assignedHeated'
|
||||||
| otherwise = id
|
| otherwise = id
|
||||||
@ -225,11 +223,9 @@ postAUsersR tid ssh ash = do
|
|||||||
& defaultPagesize PagesizeAll
|
& defaultPagesize PagesizeAll
|
||||||
|
|
||||||
usersTable <- dbTableDB' allocationUsersDBTableValidator allocationUsersDBTable
|
usersTable <- dbTableDB' allocationUsersDBTableValidator allocationUsersDBTable
|
||||||
return (usersTable, missingPriorities)
|
return usersTable
|
||||||
|
|
||||||
siteLayoutMsg MsgMenuAllocationUsers $ do
|
siteLayoutMsg MsgMenuAllocationUsers $ do
|
||||||
setTitleI $ MsgAllocationUsersTitle tid ssh ash
|
setTitleI $ MsgAllocationUsersTitle tid ssh ash
|
||||||
|
|
||||||
when missingPriorities $
|
|
||||||
notification NotificationBroad =<< messageIconI Warning IconMissingAllocationPriority MsgAllocationMissingPrioritiesIgnored
|
|
||||||
usersTable
|
usersTable
|
||||||
|
|||||||
@ -902,15 +902,22 @@ maybeAnchorColonnadeM mkUrl = imapColonnade anchorColonnade'
|
|||||||
anchorColonnade' inp (view dbCell -> (attrs, act)) = review dbCell . (attrs,) $
|
anchorColonnade' inp (view dbCell -> (attrs, act)) = review dbCell . (attrs,) $
|
||||||
view (dbCell . _2) . maybeAnchorCellM (mkUrl inp) =<< act
|
view (dbCell . _2) . maybeAnchorCellM (mkUrl inp) =<< act
|
||||||
|
|
||||||
|
|
||||||
emptyOpticColonnade :: forall h r' focus c.
|
emptyOpticColonnade :: forall h r' focus c.
|
||||||
( Monoid c
|
Monoid c
|
||||||
)
|
|
||||||
=> Getting (Endo [focus]) r' focus -- ^ View on @focus@ within @r'@ that may produce any number of results
|
=> Getting (Endo [focus]) r' focus -- ^ View on @focus@ within @r'@ that may produce any number of results
|
||||||
-> ((forall focus'. Getting focus' r' focus) -> Colonnade h r' c) -- ^ `OpticColonnade focus`
|
-> ((forall focus'. Getting focus' r' focus) -> Colonnade h r' c) -- ^ `OpticColonnade focus`
|
||||||
-> Colonnade h r' c
|
-> Colonnade h r' c
|
||||||
-- ^ Generalize an `OpticColonnade` from `Getter` to `Fold` by defaulting results of zero values to `mempty`
|
-- ^ Generalize an `OpticColonnade` from `Getter` to `Fold` by defaulting results of zero values to `mempty`
|
||||||
emptyOpticColonnade l' c
|
emptyOpticColonnade = emptyOpticColonnade' mempty
|
||||||
|
|
||||||
|
|
||||||
|
emptyOpticColonnade' :: forall h r' focus c.
|
||||||
|
c
|
||||||
|
-> Getting (Endo [focus]) r' focus -- ^ View on @focus@ within @r'@ that may produce any number of results
|
||||||
|
-> ((forall focus'. Getting focus' r' focus) -> Colonnade h r' c) -- ^ `OpticColonnade focus`
|
||||||
|
-> Colonnade h r' c
|
||||||
|
-- ^ Generalize an `OpticColonnade` from `Getter` to `Fold` by defaulting results of zero values
|
||||||
|
emptyOpticColonnade' defC l' c
|
||||||
= Colonnade $ oldColonnade <&> \column -> column { oneColonnadeEncode = \s -> defaultColumn s $ oneColonnadeEncode column }
|
= Colonnade $ oldColonnade <&> \column -> column { oneColonnadeEncode = \s -> defaultColumn s $ oneColonnadeEncode column }
|
||||||
where
|
where
|
||||||
l :: Fold r' focus
|
l :: Fold r' focus
|
||||||
@ -925,7 +932,7 @@ emptyOpticColonnade l' c
|
|||||||
defaultColumn :: r' -> (r' -> c) -> c
|
defaultColumn :: r' -> (r' -> c) -> c
|
||||||
defaultColumn x f
|
defaultColumn x f
|
||||||
| has l x = f x
|
| has l x = f x
|
||||||
| otherwise = mempty
|
| otherwise = defC
|
||||||
|
|
||||||
maybeOpticSortColumn :: OpticSortColumn (Maybe val) -> OpticSortColumn val
|
maybeOpticSortColumn :: OpticSortColumn (Maybe val) -> OpticSortColumn val
|
||||||
maybeOpticSortColumn sortColumn = \queryFocus -> sortColumn $ queryFocus . to E.just
|
maybeOpticSortColumn sortColumn = \queryFocus -> sortColumn $ queryFocus . to E.just
|
||||||
|
|||||||
Reference in New Issue
Block a user