diff --git a/src/app/components/elements/PrintButton.tsx b/src/app/components/elements/PrintButton.tsx index 3de261165c..fe617cdf30 100644 --- a/src/app/components/elements/PrintButton.tsx +++ b/src/app/components/elements/PrintButton.tsx @@ -14,8 +14,8 @@ export const PrintButton = ({questionPage}: PrintProps ) => { const dispatch = useAppDispatch(); return questionPage ? - <> - {questionPrintOpen &&
+
+ {questionPrintOpen &&
: siteSpecific( clickAwayClose && setShowShareLink(false), [setShowShareLink]); const buttonAriaLabel = showShareLink ? "Hide share link" : "Get share link"; - const linkWidth = isMobile() || reducedWidthLink ? 192 : (shareUrl.length * siteSpecific(9, 6)); + const linkWidth = isMobile() || reducedWidthLink ? siteSpecific(256, 192) : (shareUrl.length * siteSpecific(9, 6)); const showDuplicateAndEdit = gameboardId && isTutorOrAbove(user); - return
+ return
+
+ e.preventDefault()} aria-label="Share URL" /> +
+ {showShareLink && showDuplicateAndEdit && } {siteSpecific( { e.preventDefault(); toggleShareLink(); }} />, -
; }; diff --git a/src/app/components/elements/inputs/UserContextPicker.tsx b/src/app/components/elements/inputs/UserContextPicker.tsx index c55766dd70..77f3a5efbd 100644 --- a/src/app/components/elements/inputs/UserContextPicker.tsx +++ b/src/app/components/elements/inputs/UserContextPicker.tsx @@ -17,6 +17,7 @@ import { useUserViewingContext } from "../../../services"; import {selectors, transientUserContextSlice, useAppDispatch, useAppSelector,} from "../../../state"; +import classNames from "classnames"; const contextExplanationMap: {[key in CONTEXT_SOURCE]: string} = { [CONTEXT_SOURCE.TRANSIENT]: "these context picker settings", @@ -62,7 +63,7 @@ export const UserContextPicker = ({className, hideLabels = true}: {className?: s return {/* Stage Selector */} - +
{!hideLabels && } {!userContext.hasDefaultPreferences && (userContext.explanation.stage == CONTEXT_SOURCE.TRANSIENT || userContext.explanation.examBoard == CONTEXT_SOURCE.TRANSIENT) &&
-
+
; } diff --git a/src/app/components/elements/layout/SidebarLayout.tsx b/src/app/components/elements/layout/SidebarLayout.tsx index f699058795..26a26ec41f 100644 --- a/src/app/components/elements/layout/SidebarLayout.tsx +++ b/src/app/components/elements/layout/SidebarLayout.tsx @@ -67,7 +67,7 @@ const NavigationSidebar = (props: SidebarProps) => { if (isAda) return <>; const { className, ...rest } = props; - return ; + return ; }; interface ContentSidebarProps extends SidebarProps { @@ -86,9 +86,9 @@ const ContentSidebar = (props: ContentSidebarProps) => { const { className, buttonTitle, ...rest } = props; return <> {above['lg'](deviceSize) - ? + ? : <> -
+
{ incompatibleTags?: Tag[]; // tags that are removed when this tag is added dependentTags?: Tag[]; // tags that are removed when this tag is removed baseTag?: Tag; // tag to add when all tags are removed + partiallySelected?: boolean; checkboxStyle?: "tab" | "button"; bsSize?: "sm" | "lg"; } const FilterCheckbox = (props : FilterCheckboxProps) => { - const {tag, conceptFilters, setConceptFilters, tagCounts, checkboxStyle, incompatibleTags, dependentTags, baseTag, ...rest} = props; + const {tag, conceptFilters, setConceptFilters, tagCounts, checkboxStyle, incompatibleTags, dependentTags, baseTag, partiallySelected, ...rest} = props; const [checked, setChecked] = useState(conceptFilters.includes(tag)); useEffect(() => { @@ -210,7 +211,7 @@ const FilterCheckbox = (props : FilterCheckboxProps) => { const handleCheckboxChange = (checked: boolean) => { const newConceptFilters = checked - ? [...conceptFilters.filter(c => !incompatibleTags?.includes(c)), tag] + ? [...conceptFilters.filter(c => !incompatibleTags?.includes(c)), ...(!partiallySelected ? [tag] : [])] : conceptFilters.filter(c => ![tag, ...(dependentTags ?? [])].includes(c)); setConceptFilters(newConceptFilters.length > 0 ? newConceptFilters : (baseTag ? [baseTag] : [])); }; @@ -226,12 +227,17 @@ const FilterCheckbox = (props : FilterCheckboxProps) => { />; }; -const AllFiltersCheckbox = (props: Omit) => { - const { conceptFilters, setConceptFilters, tagCounts, baseTag, ...rest } = props; +const AllFiltersCheckbox = (props: Omit & {forceEnabled?: boolean}) => { + const { conceptFilters, setConceptFilters, tagCounts, baseTag, forceEnabled, ...rest } = props; const [previousFilters, setPreviousFilters] = useState(baseTag ? [baseTag] : []); return a + b, 0)} + id="all" checked={forceEnabled || baseTag ? conceptFilters.length === 1 && conceptFilters[0] === baseTag : !conceptFilters.length} + checkboxTitle="All" count={tagCounts && (baseTag ? tagCounts[baseTag.id] : Object.values(tagCounts).reduce((a, b) => a + b, 0))} onInputChange={(e) => { + if (forceEnabled) { + setConceptFilters(baseTag ? [baseTag] : []); + return; + } if (e.target.checked) { setPreviousFilters(conceptFilters); setConceptFilters(baseTag ? [baseTag] : []); @@ -272,19 +278,25 @@ export const SubjectSpecificConceptListSidebar = (props: ConceptListSidebarProps
Filter by topic
- + !isDefined(tagCounts) || tagCounts[tag.id] > 0).length === 0} + />
- {applicableTags.map(tag => - - )} + {applicableTags + .filter(tag => !isDefined(tagCounts) || tagCounts[tag.id] > 0) + .map(tag => + + ) + }
@@ -331,6 +343,7 @@ export const GenericConceptsSidebar = (props: ConceptListSidebarProps) => { conceptFilters.includes(tag))} // not quite isPartial; this is also true if all descendents selected className={classNames({"icon-checkbox-off": !isSelected, "icon icon-checkbox-partial-alt": isSelected && isPartial, "icon-checkbox-selected": isSelected && !isPartial})} /> {isSelected &&
diff --git a/src/app/components/elements/markup/markdownRendering.ts b/src/app/components/elements/markup/markdownRendering.ts index 9c2872a4f1..f343f2f156 100644 --- a/src/app/components/elements/markup/markdownRendering.ts +++ b/src/app/components/elements/markup/markdownRendering.ts @@ -6,8 +6,9 @@ MARKDOWN_RENDERER.renderer.rules.link_open = function(tokens: Remarkable.LinkOpe const href = utils.escapeHtml(tokens[idx].href || ""); const localLink = href.startsWith(window.location.origin) || href.startsWith("/") || href.startsWith("mailto:") || href.startsWith("#"); const title = tokens[idx].title ? (' title="' + utils.escapeHtml(utils.replaceEntities(tokens[idx].title || "")) + '"') : ''; + const conceptIcon = ""; if (localLink) { - return ``; + return `${isPhy && href.includes("/concepts/") ? conceptIcon : ""}`; } else { return ``; } diff --git a/src/app/components/pages/Concept.tsx b/src/app/components/pages/Concept.tsx index 3b0c6a15ce..0832f782cb 100644 --- a/src/app/components/pages/Concept.tsx +++ b/src/app/components/pages/Concept.tsx @@ -5,7 +5,7 @@ import {Col, Container, Row} from "reactstrap"; import {ShowLoading} from "../handlers/ShowLoading"; import {IsaacContent} from "../content/IsaacContent"; import {IsaacConceptPageDTO} from "../../../IsaacApiTypes"; -import {DOCUMENT_TYPE, Subject, above, below, usePreviousPageContext, isAda, isPhy, useDeviceSize, useNavigation} from "../../services"; +import {DOCUMENT_TYPE, Subject, above, below, usePreviousPageContext, isAda, isPhy, useDeviceSize, useNavigation, siteSpecific} from "../../services"; import {DocumentSubject, GameboardContext} from "../../../IsaacAppTypes"; import {RelatedContent} from "../elements/RelatedContent"; import {WithFigureNumbering} from "../elements/WithFigureNumbering"; @@ -60,9 +60,9 @@ export const Concept = withRouter(({match: {params}, location: {search}, concept @@ -73,14 +73,35 @@ export const Concept = withRouter(({match: {params}, location: {search}, concept - + {isPhy && <> +
+
+

{doc.title as string}

+ {doc.subtitle &&
{doc.subtitle}
} +
+
+ + + +
+
- {below["sm"](deviceSize) && } +
-
- - {above["md"](deviceSize) && } -
+
+ + +
+ } + + {isAda && <> + {below["sm"](deviceSize) && } + +
+ + {above["md"](deviceSize) && } +
+ } @@ -102,8 +123,6 @@ export const Concept = withRouter(({match: {params}, location: {search}, concept {isAda && doc.relatedContent && } - - {isPhy && doc.relatedContent && } diff --git a/src/app/components/pages/Concepts.tsx b/src/app/components/pages/Concepts.tsx index 0cb450792f..9a42295633 100644 --- a/src/app/components/pages/Concepts.tsx +++ b/src/app/components/pages/Concepts.tsx @@ -29,7 +29,7 @@ export const Concepts = withRouter((props: RouteComponentProps) => { }; const applicableTags = pageContext?.subject ? tags.getDirectDescendents(subjectToTagMap[pageContext.subject]) : tags.allFieldTags; - const tagCounts : Record = applicableTags.reduce((acc, t) => ({...acc, [t.id]: concepts?.filter(c => c.tags?.includes(t.id)).length || 0}), {}); + const tagCounts : Record = [...applicableTags, ...(pageContext?.subject ? [tags.getById(pageContext?.subject as TAG_ID)] : [])].reduce((acc, t) => ({...acc, [t.id]: concepts?.filter(c => c.tags?.includes(t.id)).length || 0}), {}); useEffect(() => { if (pageContext) { @@ -105,7 +105,7 @@ export const Concepts = withRouter((props: RouteComponentProps) => { : } - {isPhy &&
+ {isPhy &&
{shortcutAndFilteredSearchResults &&
Showing {shortcutAndFilteredSearchResults.length} results
} diff --git a/src/scss/common/scroll-button.scss b/src/scss/common/scroll-button.scss index b2c2975a13..be2d972e37 100644 --- a/src/scss/common/scroll-button.scss +++ b/src/scss/common/scroll-button.scss @@ -1,5 +1,6 @@ -button.btn.scroll-btn { +button.scroll-btn.scroll-btn { position: fixed; + display: flex; opacity: 0; height: 45px; width: 45px; @@ -15,8 +16,6 @@ button.btn.scroll-btn { &.is-sticky { opacity: 1; - display: flex; - // TODO: animate opacity on destruction } > img { diff --git a/src/scss/cs/button.scss b/src/scss/cs/button.scss index 71a864b992..398891db33 100644 --- a/src/scss/cs/button.scss +++ b/src/scss/cs/button.scss @@ -178,42 +178,6 @@ position: relative; z-index: 2; } - .share-link { - z-index: 1; - top: 3px; - font-family: $secondary-font !important; - display: none; - position: absolute; - border: 1px solid gray; - transform: translate(calc(30px - 100%)); - height: 42px; - width: 70%; - - input { - z-index: 1; - padding-right: 40px; - padding-left: 10px; - white-space: nowrap; - text-overflow: ellipsis; - user-select: all; - -webkit-user-select: all; - -moz-user-select: all; - -ms-user-select: all; - width: 100%; - height: 100%; - } - - &.double-height { - height: auto; - padding-bottom: 10px; - background: #fff; - text-align: center; - input { - height: 42px; - margin-bottom: 8px; - } - } - } } .print-icon { diff --git a/src/scss/cs/questions.scss b/src/scss/cs/questions.scss index dfc4a52556..cff03f80d6 100644 --- a/src/scss/cs/questions.scss +++ b/src/scss/cs/questions.scss @@ -1,19 +1,18 @@ @import "../common/questions"; -.question-actions-link-box { - $x-translation: 24px; - margin-left: -$x-translation; - float: left; - transform: translateX(7.5px + $x-translation); - height: 48px; // height of an icon button, should be a variable probably +.action-buttons-popup-container { + position: absolute; + height: 100%; + right: 30px; display: flex; flex-direction: column; justify-content: center; border: 3px solid $secondary; - padding-left: 15px; - padding-right: 32px; + padding-left: 1.5rem; + padding-right: 2rem; border-top-left-radius: 50px; border-bottom-left-radius: 50px; + z-index: 2; .question-actions-link { margin-top: 2px; // Just to vertically center the links nicely color: $secondary; @@ -22,6 +21,53 @@ margin-right: 7px; } } + + &:has(> input + a) { + flex-direction: row; + + input { + width: auto; + } + } + + a { + flex-grow: 1; + align-self: center; + } + + input { + flex-grow: 1; + white-space: nowrap; + text-overflow: ellipsis; + user-select: all; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + width: 100%; + height: 100%; + background: none; + border: none; + outline: none; + } + + & ~ .duplicate-and-edit { + position: absolute; + right: 30px; + padding: 24px 24px 0; + top: 24px; + text-align: center; + background: white; + border: 1px solid $gray-118; + border-radius: 8px; + border-top-left-radius: 0; + font-size: small; + z-index: 1; + } + + &.double-height { + background: #fff; + text-align: center; + } } // Validation response diff --git a/src/scss/phy/button.scss b/src/scss/phy/button.scss index 7e71f3ea05..e8d2802f23 100644 --- a/src/scss/phy/button.scss +++ b/src/scss/phy/button.scss @@ -376,43 +376,41 @@ a.btn { background-color: transparent; } -.share-link-icon { - - button { +.action-buttons-popup-container { + position: absolute; + display: flex; + flex-direction: column; + justify-content: space-evenly; + width: max-content; + height: 100%; + right: 50%; + align-items: center; + border: 1px solid $color-neutral-200; + border-radius: 0.5rem; + padding: 0 1.5rem 0 1rem; + z-index: 15; + background: white; + + & ~ button { position: relative; - z-index: 12; + z-index: 15; } - .share-link { - z-index: 11; - font-family: $secondary-font !important; - display: none; - position: absolute; - padding: 4px 32px 4px 8px; - transform: translate(calc(25px - 100%), -3px); - border: 1px solid $color-neutral-300; - background: #fff; - height: 58px; - width: 70%; - - input { - position: relative; - top: 9px; - white-space: nowrap; - text-overflow: ellipsis; - user-select: all; - -webkit-user-select: all; - -moz-user-select: all; - -ms-user-select: all; - width: 100%; - padding-left: 2px; - border: none; - border-bottom: 2px solid $color-neutral-200; - } + input { + white-space: nowrap; + text-overflow: ellipsis; + user-select: all; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + width: 100%; + height: max-content; + border: none; + border-bottom: 2px solid $color-neutral-200; + } - &.double-height { - height: 118px; - } + &.double-height { + height: calc(100% + 38px); } } diff --git a/src/scss/phy/questions.scss b/src/scss/phy/questions.scss index 03c8ebe6d2..4db12a892a 100644 --- a/src/scss/phy/questions.scss +++ b/src/scss/phy/questions.scss @@ -1,33 +1,5 @@ @import "../common/questions"; -.question-actions { - .question-actions-icon { - height: 3rem; - &:focus { - outline: none; - } - } - .question-actions-link-box { - float:right; - border: 1px solid gray; - height: 3.6rem; - border-right: none; - padding-right: 2.5rem; - display: flex; - flex-direction: column; - justify-content: center; - margin-right: -1.9rem; - background: white; - - .question-actions-link { - padding-left: 3px; - padding-right: 3px; - margin-left: 1rem; - font-style: italic; - } - } -} - .question-panel > .content-chunk > .content-value, .question-component .question-content { font-family: $primary-font; diff --git a/src/scss/phy/typography.scss b/src/scss/phy/typography.scss index ee261b2647..f384a769f6 100644 --- a/src/scss/phy/typography.scss +++ b/src/scss/phy/typography.scss @@ -246,5 +246,16 @@ a { clear: left; text-decoration: none; } + + &.a-link:has(> i.icon) { + display: inline-flex; + align-items: center; + font-weight: bold; + gap: 4px; + > i.icon { + order: 1; + margin-top: 1px; + } + } }