]> Untitled Git - lemmy-ui.git/commitdiff
Merge branch 'main' into route-data-refactor
authorSleeplessOne1917 <abias1122@gmail.com>
Sat, 17 Jun 2023 02:22:36 +0000 (02:22 +0000)
committerGitHub <noreply@github.com>
Sat, 17 Jun 2023 02:22:36 +0000 (02:22 +0000)
lemmy-translations
src/assets/css/main.css
src/assets/css/themes/darkly.css
src/assets/css/themes/litely.css
src/shared/components/app/app.tsx
src/shared/components/common/language-select.tsx
src/shared/components/common/markdown-textarea.tsx
src/shared/components/post/post-listing.tsx
src/shared/utils.ts

index f45ddff206adb52ab0ac7555bf14978edac5d2f2..c9a07885f35cf334d3cf167cb57587a8177fc3fb 160000 (submodule)
@@ -1 +1 @@
-Subproject commit f45ddff206adb52ab0ac7555bf14978edac5d2f2
+Subproject commit c9a07885f35cf334d3cf167cb57587a8177fc3fb
index 82f8433e89d063fd4c514dc9e5a09664fdbece4d..da3f7ffc630be20157ac817455b451bcc4694e6a 100644 (file)
@@ -46,7 +46,7 @@
 }
 
 .md-div p:last-child {
-  margin-bottom: 0px;
+  margin-bottom: 0;
 }
 
 .md-div img {
@@ -371,7 +371,7 @@ br.big {
 }
 
 .tribute-container li {
-  padding: 5px 5px;
+  padding: 5px;
   cursor: pointer;
 }
 
@@ -410,13 +410,22 @@ br.big {
   -webkit-line-clamp: 3;
   -webkit-box-orient: vertical;
 }
-.lang-select-action {
-  width: 100px;
+
+.emoji-picker {
+  width: 100%;
 }
 
-.lang-select-action:focus {
-  width: auto;
+.skip-link {
+  top: -40px;
+  transition: top 0.3s ease;
 }
-em-emoji-picker {
-  width: 100%;
+
+@media (prefers-reduced-motion: reduce) {
+  .skip-link {
+    transition: none;
+  }
+}
+
+.skip-link:focus {
+  top: 0;
 }
index a2b0917790c5abc8414bfbe3d41d61b3d94fa8a8..49399db2ccfab37c5952aea90f7ef0b45113ff0c 100644 (file)
@@ -19,6 +19,7 @@
   --warning: #f39c12;
   --danger: #e74c3c;
   --light: #303030;
+  --medium-light: var(--secondary);
   --dark: #dee2e6;
   --breakpoint-xs: 0;
   --breakpoint-sm: 576px;
index 9f7d685db952b83c5b0033f45775a53cc7e8701e..f9d2b2d3c3de9b433aa433dc075cc132e32e6616 100644 (file)
@@ -19,6 +19,7 @@
   --warning: #ffc107;
   --danger: #873208;
   --light: #f8f9fa;
+  --medium-light: var(--bs-gray-300);
   --dark: #343a40;
   --breakpoint-xs: 0;
   --breakpoint-sm: 576px;
index c495548c679e7bfc0460882ce783df704686c38d..79aa77eb2dc07a0d2930f4c6ec3dfa6098839fc7 100644 (file)
@@ -1,4 +1,4 @@
-import { Component } from "inferno";
+import { Component, createRef, linkEvent, RefObject } from "inferno";
 import { Provider } from "inferno-i18next-dess";
 import { Route, Switch } from "inferno-router";
 import { i18n } from "../../i18next";
@@ -15,8 +15,15 @@ import { Theme } from "./theme";
 
 export class App extends Component<any, any> {
   private isoData: IsoDataOptionalSite = setIsoData(this.context);
+  private readonly mainContentRef: RefObject<HTMLElement>;
   constructor(props: any, context: any) {
     super(props, context);
+    this.mainContentRef = createRef();
+  }
+
+  handleJumpToContent(event) {
+    event.preventDefault();
+    this.mainContentRef.current?.focus();
   }
   render() {
     const siteRes = this.isoData.site_res;
@@ -26,6 +33,12 @@ export class App extends Component<any, any> {
       <>
         <Provider i18next={i18n}>
           <div id="app" className="lemmy-site">
+            <a
+              className="skip-link bg-light text-dark p-2 text-decoration-none position-absolute start-0 z-3"
+              onClick={linkEvent(this, this.handleJumpToContent)}
+            >
+              ${i18n.t("jump_to_content", "Jump to content")}
+            </a>
             {siteView && (
               <Theme defaultTheme={siteView.local_site.default_theme} />
             )}
@@ -39,14 +52,16 @@ export class App extends Component<any, any> {
                     exact
                     component={routeProps => (
                       <ErrorGuard>
-                        {RouteComponent &&
-                          (isAuthPath(path ?? "") ? (
-                            <AuthGuard>
+                        <main tabIndex={-1} ref={this.mainContentRef}>
+                          {RouteComponent &&
+                            (isAuthPath(path ?? "") ? (
+                              <AuthGuard>
+                                <RouteComponent {...routeProps} />
+                              </AuthGuard>
+                            ) : (
                               <RouteComponent {...routeProps} />
-                            </AuthGuard>
-                          ) : (
-                            <RouteComponent {...routeProps} />
-                          ))}
+                            ))}
+                        </main>
                       </ErrorGuard>
                     )}
                   />
index fac3216f8dd5f267f773df1e664beb0bb01d776d..09e9c968dbf89c524409fff573b165dc920bb475 100644 (file)
@@ -100,12 +100,9 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
 
     return (
       <select
-        className={classNames(
-          "lang-select-action",
-          this.props.iconVersion
-            ? "btn btn-sm text-muted"
-            : "form-control custom-select"
-        )}
+        className={classNames("lang-select-action", {
+          "form-control custom-select": !this.props.iconVersion,
+        })}
         id={this.id}
         onChange={linkEvent(this, this.handleLanguageChange)}
         aria-label={i18n.t("language_select_placeholder")}
index a3a9cbde61d624eb370fb425447723ade8bb3444..ef7ba0187df3d4a7e9c0916eb0c5fd5df22672c6 100644 (file)
@@ -1,4 +1,5 @@
 import autosize from "autosize";
+import classNames from "classnames";
 import { NoOptionI18nKeys } from "i18next";
 import { Component, linkEvent } from "inferno";
 import { Language } from "lemmy-js-client";
@@ -144,101 +145,123 @@ export class MarkdownTextArea extends Component<
           }
         />
         <div className="form-group row">
-          <div className={`col-sm-12`}>
-            <textarea
-              id={this.id}
-              className={`form-control ${this.state.previewMode && "d-none"}`}
-              value={this.state.content}
-              onInput={linkEvent(this, this.handleContentChange)}
-              onPaste={linkEvent(this, this.handleImageUploadPaste)}
-              onKeyDown={linkEvent(this, this.handleKeyBinds)}
-              required
-              disabled={this.isDisabled}
-              rows={2}
-              maxLength={this.props.maxLength ?? markdownFieldCharacterLimit}
-              placeholder={this.props.placeholder}
-            />
-            {this.state.previewMode && this.state.content && (
+          <div className="col-12">
+            <div
+              className="rounded bg-light overflow-hidden"
+              style={{
+                border: "1px solid var(--medium-light)",
+              }}
+            >
               <div
-                className="card border-secondary card-body md-div"
-                dangerouslySetInnerHTML={mdToHtml(this.state.content)}
-              />
-            )}
-            {this.state.imageUploadStatus &&
-              this.state.imageUploadStatus.total > 1 && (
-                <ProgressBar
-                  className="mt-2"
-                  striped
-                  animated
-                  value={this.state.imageUploadStatus.uploaded}
-                  max={this.state.imageUploadStatus.total}
-                  text={i18n.t("pictures_uploded_progess", {
-                    uploaded: this.state.imageUploadStatus.uploaded,
-                    total: this.state.imageUploadStatus.total,
+                className="d-flex flex-wrap"
+                style={{
+                  "border-bottom": "1px solid var(--medium-light)",
+                }}
+              >
+                {this.getFormatButton("bold", this.handleInsertBold)}
+                {this.getFormatButton("italic", this.handleInsertItalic)}
+                {this.getFormatButton("link", this.handleInsertLink)}
+                <EmojiPicker
+                  onEmojiClick={e => this.handleEmoji(this, e)}
+                  disabled={this.isDisabled}
+                ></EmojiPicker>
+                <form className="btn btn-sm text-muted font-weight-bold">
+                  <label
+                    htmlFor={`file-upload-${this.id}`}
+                    className={`mb-0 ${
+                      UserService.Instance.myUserInfo && "pointer"
+                    }`}
+                    data-tippy-content={i18n.t("upload_image")}
+                  >
+                    {this.state.imageUploadStatus ? (
+                      <Spinner />
+                    ) : (
+                      <Icon icon="image" classes="icon-inline" />
+                    )}
+                  </label>
+                  <input
+                    id={`file-upload-${this.id}`}
+                    type="file"
+                    accept="image/*,video/*"
+                    name="file"
+                    className="d-none"
+                    multiple
+                    disabled={
+                      !UserService.Instance.myUserInfo || this.isDisabled
+                    }
+                    onChange={linkEvent(this, this.handleImageUpload)}
+                  />
+                </form>
+                {this.getFormatButton("header", this.handleInsertHeader)}
+                {this.getFormatButton(
+                  "strikethrough",
+                  this.handleInsertStrikethrough
+                )}
+                {this.getFormatButton("quote", this.handleInsertQuote)}
+                {this.getFormatButton("list", this.handleInsertList)}
+                {this.getFormatButton("code", this.handleInsertCode)}
+                {this.getFormatButton("subscript", this.handleInsertSubscript)}
+                {this.getFormatButton(
+                  "superscript",
+                  this.handleInsertSuperscript
+                )}
+                {this.getFormatButton("spoiler", this.handleInsertSpoiler)}
+                <a
+                  href={markdownHelpUrl}
+                  className="btn btn-sm text-muted font-weight-bold"
+                  title={i18n.t("formatting_help")}
+                  rel={relTags}
+                >
+                  <Icon icon="help-circle" classes="icon-inline" />
+                </a>
+              </div>
+
+              <div>
+                <textarea
+                  id={this.id}
+                  className={classNames("form-control border-0 rounded-0", {
+                    "d-none": this.state.previewMode,
                   })}
+                  value={this.state.content}
+                  onInput={linkEvent(this, this.handleContentChange)}
+                  onPaste={linkEvent(this, this.handleImageUploadPaste)}
+                  onKeyDown={linkEvent(this, this.handleKeyBinds)}
+                  required
+                  disabled={this.isDisabled}
+                  rows={2}
+                  maxLength={
+                    this.props.maxLength ?? markdownFieldCharacterLimit
+                  }
+                  placeholder={this.props.placeholder}
                 />
-              )}
-          </div>
-          <label className="sr-only" htmlFor={this.id}>
-            {i18n.t("body")}
-          </label>
-        </div>
-        <div className="row">
-          <div className="col-sm-12 d-flex flex-wrap">
-            {this.getFormatButton("bold", this.handleInsertBold)}
-            {this.getFormatButton("italic", this.handleInsertItalic)}
-            {this.getFormatButton("link", this.handleInsertLink)}
-            <EmojiPicker
-              onEmojiClick={e => this.handleEmoji(this, e)}
-              disabled={this.isDisabled}
-            ></EmojiPicker>
-            <form className="btn btn-sm text-muted font-weight-bold">
-              <label
-                htmlFor={`file-upload-${this.id}`}
-                className={`mb-0 ${
-                  UserService.Instance.myUserInfo && "pointer"
-                }`}
-                data-tippy-content={i18n.t("upload_image")}
-              >
-                {this.state.imageUploadStatus ? (
-                  <Spinner />
-                ) : (
-                  <Icon icon="image" classes="icon-inline" />
+                {this.state.previewMode && this.state.content && (
+                  <div
+                    className="card border-secondary card-body md-div"
+                    dangerouslySetInnerHTML={mdToHtml(this.state.content)}
+                  />
                 )}
+                {this.state.imageUploadStatus &&
+                  this.state.imageUploadStatus.total > 1 && (
+                    <ProgressBar
+                      className="mt-2"
+                      striped
+                      animated
+                      value={this.state.imageUploadStatus.uploaded}
+                      max={this.state.imageUploadStatus.total}
+                      text={i18n.t("pictures_uploded_progess", {
+                        uploaded: this.state.imageUploadStatus.uploaded,
+                        total: this.state.imageUploadStatus.total,
+                      })}
+                    />
+                  )}
+              </div>
+              <label className="sr-only" htmlFor={this.id}>
+                {i18n.t("body")}
               </label>
-              <input
-                id={`file-upload-${this.id}`}
-                type="file"
-                accept="image/*,video/*"
-                name="file"
-                className="d-none"
-                multiple
-                disabled={!UserService.Instance.myUserInfo || this.isDisabled}
-                onChange={linkEvent(this, this.handleImageUpload)}
-              />
-            </form>
-            {this.getFormatButton("header", this.handleInsertHeader)}
-            {this.getFormatButton(
-              "strikethrough",
-              this.handleInsertStrikethrough
-            )}
-            {this.getFormatButton("quote", this.handleInsertQuote)}
-            {this.getFormatButton("list", this.handleInsertList)}
-            {this.getFormatButton("code", this.handleInsertCode)}
-            {this.getFormatButton("subscript", this.handleInsertSubscript)}
-            {this.getFormatButton("superscript", this.handleInsertSuperscript)}
-            {this.getFormatButton("spoiler", this.handleInsertSpoiler)}
-            <a
-              href={markdownHelpUrl}
-              className="btn btn-sm text-muted font-weight-bold"
-              title={i18n.t("formatting_help")}
-              rel={relTags}
-            >
-              <Icon icon="help-circle" classes="icon-inline" />
-            </a>
+            </div>
           </div>
 
-          <div className="col-sm-12 d-flex align-items-center flex-wrap">
+          <div className="col-12 d-flex align-items-center flex-wrap mt-2">
             {this.props.showLanguage && (
               <LanguageSelect
                 iconVersion
@@ -258,7 +281,7 @@ export class MarkdownTextArea extends Component<
             {this.props.buttonTitle && (
               <button
                 type="submit"
-                className="btn btn-sm btn-secondary mr-2"
+                className="btn btn-sm btn-secondary ml-2"
                 disabled={this.isDisabled}
               >
                 {this.state.loading ? (
@@ -271,7 +294,7 @@ export class MarkdownTextArea extends Component<
             {this.props.replyType && (
               <button
                 type="button"
-                className="btn btn-sm btn-secondary mr-2"
+                className="btn btn-sm btn-secondary ml-2"
                 onClick={linkEvent(this, this.handleReplyCancel)}
               >
                 {i18n.t("cancel")}
@@ -279,7 +302,7 @@ export class MarkdownTextArea extends Component<
             )}
             {this.state.content && (
               <button
-                className={`btn btn-sm btn-secondary mr-2 ${
+                className={`btn btn-sm btn-secondary ml-2 ${
                   this.state.previewMode && "active"
                 }`}
                 onClick={linkEvent(this, this.handlePreviewToggle)}
index e170a3321b973c606617d70598162bee7391f8f8..b9d4125075fff34fb8d9b1eaba4156205dfdcf4f 100644 (file)
@@ -631,7 +631,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     const post = this.postView.post;
 
     return (
-      <div className="d-flex justify-content-start flex-wrap text-muted font-weight-bold mb-1">
+      <div className="d-flex align-items-center justify-content-start flex-wrap text-muted font-weight-bold mb-1">
         {this.commentsButton}
         {canShare() && (
           <button
index 7007a214ffdeb67a48338ae3b6ed2b5dae29ee38..c7fbca6b5654db597b098c452af5dc6ed522273b 100644 (file)
@@ -334,7 +334,12 @@ export function isVideo(url: string) {
 }
 
 export function validURL(str: string) {
-  return !!new URL(str);
+  try {
+    new URL(str);
+    return true;
+  } catch {
+    return false;
+  }
 }
 
 export function validInstanceTLD(str: string) {