Adjust editor config (#976)

* Adjust editor config

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
Co-authored-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Tilman Vatteroth 2021-02-03 22:13:04 +01:00 committed by GitHub
parent 0180c75e55
commit e12dc523f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
301 changed files with 4393 additions and 3741 deletions

View file

@ -51,31 +51,39 @@ ij_scss_hex_color_upper_case = false
ij_scss_keep_blank_lines_in_code = 2
ij_scss_keep_indents_on_empty_lines = false
ij_scss_keep_single_line_blocks = false
ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
ij_scss_space_after_colon = true
ij_scss_space_before_opening_brace = true
ij_scss_use_double_quotes = true
ij_scss_value_alignment = 0
[{*.ats,*.ts,*.tsx}]
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ats, *.ts, *.tsx}]
indent_size = 2
tab_width = 2
ij_continuation_indent_size = 2
ij_typescript_align_imports = false
ij_typescript_align_multiline_array_initializer_expression = false
ij_typescript_align_multiline_binary_operation = false
ij_typescript_align_multiline_chained_methods = false
ij_typescript_align_multiline_chained_methods = true
ij_typescript_align_multiline_extends_list = false
ij_typescript_align_multiline_for = true
ij_typescript_align_multiline_parameters = true
ij_typescript_align_multiline_parameters = false
ij_typescript_align_multiline_parameters_in_calls = false
ij_typescript_align_multiline_ternary_operation = false
ij_typescript_align_object_properties = 0
ij_typescript_align_union_types = false
ij_typescript_align_union_types = true
ij_typescript_align_var_statements = 0
ij_typescript_array_initializer_new_line_after_left_brace = false
ij_typescript_array_initializer_right_brace_on_new_line = false
ij_typescript_array_initializer_wrap = off
ij_typescript_array_initializer_wrap = normal
ij_typescript_assignment_wrap = off
ij_typescript_binary_operation_sign_on_next_line = false
ij_typescript_binary_operation_wrap = off
@ -95,26 +103,26 @@ ij_typescript_catch_on_new_line = false
ij_typescript_chained_call_dot_on_new_line = true
ij_typescript_class_brace_style = end_of_line
ij_typescript_comma_on_new_line = false
ij_typescript_do_while_brace_force = never
ij_typescript_do_while_brace_force = always
ij_typescript_else_on_new_line = false
ij_typescript_enforce_trailing_comma = keep
ij_typescript_enforce_trailing_comma = remove
ij_typescript_extends_keyword_wrap = off
ij_typescript_extends_list_wrap = off
ij_typescript_field_prefix = _
ij_typescript_file_name_style = relaxed
ij_typescript_finally_on_new_line = false
ij_typescript_for_brace_force = never
ij_typescript_for_brace_force = always
ij_typescript_for_statement_new_line_after_left_paren = false
ij_typescript_for_statement_right_paren_on_new_line = false
ij_typescript_for_statement_wrap = off
ij_typescript_force_quote_style = false
ij_typescript_force_quote_style = true
ij_typescript_force_semicolon_style = true
ij_typescript_function_expression_brace_style = end_of_line
ij_typescript_if_brace_force = never
ij_typescript_if_brace_force = always
ij_typescript_import_merge_members = global
ij_typescript_import_prefer_absolute_path = global
ij_typescript_import_sort_members = true
ij_typescript_import_sort_module_name = true
ij_typescript_import_sort_module_name = false
ij_typescript_import_use_node_resolution = true
ij_typescript_imports_wrap = on_every_item
ij_typescript_indent_case_from_switch = true
@ -122,7 +130,7 @@ ij_typescript_indent_chained_calls = true
ij_typescript_indent_package_children = 0
ij_typescript_jsdoc_include_types = false
ij_typescript_jsx_attribute_value = braces
ij_typescript_keep_blank_lines_in_code = 1
ij_typescript_keep_blank_lines_in_code = 2
ij_typescript_keep_first_column_comment = true
ij_typescript_keep_indents_on_empty_lines = false
ij_typescript_keep_line_breaks = true
@ -131,11 +139,11 @@ ij_typescript_keep_simple_methods_in_one_line = false
ij_typescript_line_comment_add_space = true
ij_typescript_line_comment_at_first_column = false
ij_typescript_method_brace_style = end_of_line
ij_typescript_method_call_chain_wrap = off
ij_typescript_method_call_chain_wrap = split_into_lines
ij_typescript_method_parameters_new_line_after_left_paren = false
ij_typescript_method_parameters_right_paren_on_new_line = false
ij_typescript_method_parameters_wrap = off
ij_typescript_object_literal_wrap = on_every_item
ij_typescript_object_literal_wrap = off
ij_typescript_parentheses_expression_new_line_after_left_paren = false
ij_typescript_parentheses_expression_right_paren_on_new_line = false
ij_typescript_place_assignment_sign_on_next_line = false
@ -144,7 +152,7 @@ ij_typescript_prefer_explicit_types_function_expression_returns = false
ij_typescript_prefer_explicit_types_function_returns = false
ij_typescript_prefer_explicit_types_vars_fields = false
ij_typescript_prefer_parameters_wrap = false
ij_typescript_reformat_c_style_comments = false
ij_typescript_reformat_c_style_comments = true
ij_typescript_space_after_colon = true
ij_typescript_space_after_comma = true
ij_typescript_space_after_dots_in_rest_parameter = false
@ -175,7 +183,7 @@ ij_typescript_space_before_if_left_brace = true
ij_typescript_space_before_if_parentheses = true
ij_typescript_space_before_method_call_parentheses = false
ij_typescript_space_before_method_left_brace = true
ij_typescript_space_before_method_parentheses = true
ij_typescript_space_before_method_parentheses = false
ij_typescript_space_before_property_colon = false
ij_typescript_space_before_quest = true
ij_typescript_space_before_switch_left_brace = true
@ -202,7 +210,7 @@ ij_typescript_spaces_within_catch_parentheses = false
ij_typescript_spaces_within_for_parentheses = false
ij_typescript_spaces_within_if_parentheses = false
ij_typescript_spaces_within_imports = true
ij_typescript_spaces_within_interpolation_expressions = false
ij_typescript_spaces_within_interpolation_expressions = true
ij_typescript_spaces_within_method_call_parentheses = false
ij_typescript_spaces_within_method_parentheses = false
ij_typescript_spaces_within_object_literal_braces = true
@ -215,15 +223,15 @@ ij_typescript_spaces_within_while_parentheses = false
ij_typescript_special_else_if_treatment = true
ij_typescript_ternary_operation_signs_on_next_line = false
ij_typescript_ternary_operation_wrap = off
ij_typescript_union_types_wrap = on_every_item
ij_typescript_union_types_wrap = normal
ij_typescript_use_chained_calls_group_indents = false
ij_typescript_use_double_quotes = false
ij_typescript_use_explicit_js_extension = global
ij_typescript_use_path_mapping = always
ij_typescript_use_public_modifier = false
ij_typescript_use_semicolon_after_statement = false
ij_typescript_var_declaration_wrap = normal
ij_typescript_while_brace_force = never
ij_typescript_var_declaration_wrap = on_every_item
ij_typescript_while_brace_force = always
ij_typescript_while_on_new_line = false
ij_typescript_wrap_comments = false
@ -275,8 +283,8 @@ ij_javascript_for_brace_force = never
ij_javascript_for_statement_new_line_after_left_paren = false
ij_javascript_for_statement_right_paren_on_new_line = false
ij_javascript_for_statement_wrap = off
ij_javascript_force_quote_style = true
ij_javascript_force_semicolon_style = true
ij_javascript_force_quote_style = false
ij_javascript_force_semicolon_style = false
ij_javascript_function_expression_brace_style = end_of_line
ij_javascript_if_brace_force = never
ij_javascript_import_merge_members = global
@ -289,12 +297,12 @@ ij_javascript_indent_case_from_switch = true
ij_javascript_indent_chained_calls = true
ij_javascript_indent_package_children = 0
ij_javascript_jsx_attribute_value = braces
ij_javascript_keep_blank_lines_in_code = 1
ij_javascript_keep_blank_lines_in_code = 2
ij_javascript_keep_first_column_comment = true
ij_javascript_keep_indents_on_empty_lines = false
ij_javascript_keep_line_breaks = true
ij_javascript_keep_simple_blocks_in_one_line = true
ij_javascript_keep_simple_methods_in_one_line = true
ij_javascript_keep_simple_blocks_in_one_line = false
ij_javascript_keep_simple_methods_in_one_line = false
ij_javascript_line_comment_add_space = true
ij_javascript_line_comment_at_first_column = false
ij_javascript_method_brace_style = end_of_line
@ -337,12 +345,12 @@ ij_javascript_space_before_for_left_brace = true
ij_javascript_space_before_for_parentheses = true
ij_javascript_space_before_for_semicolon = false
ij_javascript_space_before_function_left_parenth = true
ij_javascript_space_before_generator_mult = true
ij_javascript_space_before_generator_mult = false
ij_javascript_space_before_if_left_brace = true
ij_javascript_space_before_if_parentheses = true
ij_javascript_space_before_method_call_parentheses = false
ij_javascript_space_before_method_left_brace = true
ij_javascript_space_before_method_parentheses = true
ij_javascript_space_before_method_parentheses = false
ij_javascript_space_before_property_colon = false
ij_javascript_space_before_quest = true
ij_javascript_space_before_switch_left_brace = true
@ -368,11 +376,11 @@ ij_javascript_spaces_within_brackets = false
ij_javascript_spaces_within_catch_parentheses = false
ij_javascript_spaces_within_for_parentheses = false
ij_javascript_spaces_within_if_parentheses = false
ij_javascript_spaces_within_imports = true
ij_javascript_spaces_within_imports = false
ij_javascript_spaces_within_interpolation_expressions = false
ij_javascript_spaces_within_method_call_parentheses = false
ij_javascript_spaces_within_method_parentheses = false
ij_javascript_spaces_within_object_literal_braces = true
ij_javascript_spaces_within_object_literal_braces = false
ij_javascript_spaces_within_object_type_braces = true
ij_javascript_spaces_within_parentheses = false
ij_javascript_spaces_within_switch_parentheses = false
@ -380,22 +388,22 @@ ij_javascript_spaces_within_type_assertion = false
ij_javascript_spaces_within_union_types = true
ij_javascript_spaces_within_while_parentheses = false
ij_javascript_special_else_if_treatment = true
ij_javascript_ternary_operation_signs_on_next_line = true
ij_javascript_ternary_operation_signs_on_next_line = false
ij_javascript_ternary_operation_wrap = off
ij_javascript_union_types_wrap = on_every_item
ij_javascript_use_chained_calls_group_indents = false
ij_javascript_use_double_quotes = false
ij_javascript_use_double_quotes = true
ij_javascript_use_explicit_js_extension = global
ij_javascript_use_path_mapping = always
ij_javascript_use_public_modifier = false
ij_javascript_use_semicolon_after_statement = false
ij_javascript_use_semicolon_after_statement = true
ij_javascript_var_declaration_wrap = normal
ij_javascript_while_brace_force = never
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
indent_size = 4
indent_size = 2
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
@ -423,7 +431,7 @@ ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_keep_whitespaces_inside = span, pre, textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
@ -433,9 +441,30 @@ ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
ij_html_uniform_ident = false
[{*.yaml,*.yml}]
[{*.markdown, *.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
[{*.yaml, *.yml}]
indent_size = 2
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true

View file

@ -24,7 +24,9 @@ describe('Banner', () => {
.find('.fa-times')
.click()
.then(() => {
expect(localStorage.getItem('bannerTimeStamp')).to.equal(banner.timestamp)
expect(localStorage.getItem('bannerTimeStamp'))
.to
.equal(banner.timestamp)
})
cy.get('.alert-primary.show')
.should('not.exist')

View file

@ -16,73 +16,73 @@ describe('Document Title', () => {
describe('title should be yaml metadata title', () => {
it('just yaml metadata title', () => {
cy.codemirrorFill(`---\ntitle: ${title}\n---`)
cy.codemirrorFill(`---\ntitle: ${ title }\n---`)
cy.title()
.should('eq', `${title} - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
})
it('yaml metadata title and opengraph title', () => {
cy.codemirrorFill(`---\ntitle: ${title}\nopengraph:\n title: False title\n---`)
cy.codemirrorFill(`---\ntitle: ${ title }\nopengraph:\n title: False title\n---`)
cy.title()
.should('eq', `${title} - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
})
it('yaml metadata title, opengraph title and first heading', () => {
cy.codemirrorFill(`---\ntitle: ${title}\nopengraph:\n title: False title\n---\n# a first title`)
cy.codemirrorFill(`---\ntitle: ${ title }\nopengraph:\n title: False title\n---\n# a first title`)
cy.title()
.should('eq', `${title} - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
})
})
describe('title should be opengraph title', () => {
it('just opengraph title', () => {
cy.codemirrorFill(`---\nopengraph:\n title: ${title}\n---`)
cy.codemirrorFill(`---\nopengraph:\n title: ${ title }\n---`)
cy.title()
.should('eq', `${title} - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
})
it('opengraph title and first heading', () => {
cy.codemirrorFill(`---\nopengraph:\n title: ${title}\n---\n# a first title`)
cy.codemirrorFill(`---\nopengraph:\n title: ${ title }\n---\n# a first title`)
cy.title()
.should('eq', `${title} - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
})
})
describe('title should be first heading', () => {
it('just first heading', () => {
cy.codemirrorFill(`# ${title}`)
cy.codemirrorFill(`# ${ title }`)
cy.title()
.should('eq', `${title} - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
})
it('just first heading with alt-text instead of image', () => {
cy.codemirrorFill(`# ${title} ![abc](https://dummyimage.com/48)`)
cy.codemirrorFill(`# ${ title } ![abc](https://dummyimage.com/48)`)
cy.title()
.should('eq', `${title} abc - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } abc - HedgeDoc @ ${ branding.name }`)
})
it('just first heading without link syntax', () => {
cy.codemirrorFill(`# ${title} [link](https://hedgedoc.org)`)
cy.codemirrorFill(`# ${ title } [link](https://hedgedoc.org)`)
cy.title()
.should('eq', `${title} link - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } link - HedgeDoc @ ${ branding.name }`)
})
it('markdown syntax removed first', () => {
cy.codemirrorFill(`# ${title} 1*2*3 4*5**`)
cy.codemirrorFill(`# ${ title } 1*2*3 4*5**`)
cy.title()
.should('eq', `${title} 123 4*5** - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } 123 4*5** - HedgeDoc @ ${ branding.name }`)
})
it('markdown syntax removed second', () => {
cy.codemirrorFill(`# ${title} **1 2*`)
cy.codemirrorFill(`# ${ title } **1 2*`)
cy.title()
.should('eq', `${title} *1 2 - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } *1 2 - HedgeDoc @ ${ branding.name }`)
})
it('markdown syntax removed third', () => {
cy.codemirrorFill(`# ${title} _asd_`)
cy.codemirrorFill(`# ${ title } _asd_`)
cy.title()
.should('eq', `${title} asd - HedgeDoc @ ${branding.name}`)
.should('eq', `${ title } asd - HedgeDoc @ ${ branding.name }`)
})
it('katex code looks right', () => {
@ -93,7 +93,7 @@ describe('Document Title', () => {
cy.get('.CodeMirror textarea')
.type('{Enter}{Enter}{Enter}{Enter}{Enter}') //This is a workaround because I don't know how to make sure, that the title gets updated in time.
cy.title()
.should('eq', `α-foo - HedgeDoc @ ${branding.name}`)
.should('eq', `α-foo - HedgeDoc @ ${ branding.name }`)
})
})
})

View file

@ -6,7 +6,7 @@
describe('Export', () => {
const testTitle = 'testContent'
const testContent = `---\ntitle: ${testTitle}\n---\nThis is some test content`
const testContent = `---\ntitle: ${ testTitle }\n---\nThis is some test content`
beforeEach(() => {
cy.visitTestEditor()

View file

@ -45,35 +45,40 @@ describe('File upload', () => {
cy.get('div.btn-group > input[type=file]')
.attachFile({ filePath: 'acme.png', mimeType: 'image/png' })
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${imageUrl})`)
.should('have.text', `![](${ imageUrl })`)
})
it('via paste', () => {
cy.fixture('acme.png').then((image: string) => {
const pasteEvent = {
clipboardData: {
files: [Cypress.Blob.base64StringToBlob(image, 'image/png')]
cy.fixture('acme.png')
.then((image: string) => {
const pasteEvent = {
clipboardData: {
files: [Cypress.Blob.base64StringToBlob(image, 'image/png')]
}
}
}
cy.get('.CodeMirror-scroll').trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${imageUrl})`)
})
cy.get('.CodeMirror-scroll')
.trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ imageUrl })`)
})
})
it('via drag and drop', () => {
cy.fixture('acme.png').then((image: string) => {
const dropEvent = {
dataTransfer: {
files: [Cypress.Blob.base64StringToBlob(image, 'image/png')],
effectAllowed: 'uninitialized'
cy.fixture('acme.png')
.then((image: string) => {
const dropEvent = {
dataTransfer: {
files: [Cypress.Blob.base64StringToBlob(image, 'image/png')],
effectAllowed: 'uninitialized'
}
}
}
cy.get('.CodeMirror-scroll').trigger('dragenter', dropEvent)
cy.get('.CodeMirror-scroll').trigger('drop', dropEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${imageUrl})`)
})
cy.get('.CodeMirror-scroll')
.trigger('dragenter', dropEvent)
cy.get('.CodeMirror-scroll')
.trigger('drop', dropEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ imageUrl })`)
})
})
})
@ -86,10 +91,11 @@ describe('File upload', () => {
})
cy.get('.fa-upload')
.click()
cy.fixture('acme.png').then(() => {
cy.get('input[type=file]')
.attachFile({ filePath: 'acme.png', mimeType: 'image/png' })
})
cy.fixture('acme.png')
.then(() => {
cy.get('input[type=file]')
.attachFile({ filePath: 'acme.png', mimeType: 'image/png' })
})
cy.get('.CodeMirror-activeline > .CodeMirror-line > span > span')
.should('have.text', String.fromCharCode(8203)) //thanks codemirror....
})
@ -101,8 +107,9 @@ describe('File upload', () => {
getData: (type = 'text') => testText
}
}
cy.get('.CodeMirror-scroll').trigger('paste', pasteEvent)
cy.get('.CodeMirror-scroll')
.trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${testText}`)
.should('have.text', `${ testText }`)
})
})

View file

@ -6,8 +6,8 @@
const findHljsCodeBlock = () => {
return cy.getMarkdownBody()
.find('pre > code.hljs')
.should('be.visible')
.find('pre > code.hljs')
.should('be.visible')
}
describe('Code', () => {
@ -160,14 +160,15 @@ describe('Code', () => {
return cy.wrap(null)
}
cy.spy(frame.contentWindow.navigator.clipboard, 'writeText').as("copy")
cy.spy(frame.contentWindow.navigator.clipboard, 'writeText')
.as('copy')
})
cy.getMarkdownRenderer()
.find('[data-cy="copy-code-button"]')
.click()
cy.get("@copy")
cy.get('@copy')
.should('be.calledWithExactly', 'let x = 0\n')
})
})

View file

@ -17,7 +17,8 @@ describe('Languages', () => {
cy.get('@languages')
.should('have.length', 28)
languages.forEach(language => {
cy.get('@languages').contains(language)
cy.get('@languages')
.contains(language)
})
})

View file

@ -64,14 +64,16 @@ describe('Links Intro', () => {
describe('Menu Buttons logged in', () => {
it('New note', () => {
cy.get('.d-inline-flex.btn-primary').click()
cy.get('.d-inline-flex.btn-primary')
.click()
cy.url()
.should('include', '/new')
})
describe('User Menu', () => {
beforeEach(() => {
cy.get('#dropdown-user').click()
cy.get('#dropdown-user')
.click()
})
it('Features', () => {

View file

@ -7,7 +7,7 @@
describe('The status bar text length info', () => {
const warningTestContent = ('0123456789'.repeat(10))
const dangerTestContent = ('0123456789'.repeat(20))
const tooMuchTestContent = `${dangerTestContent}a`
const tooMuchTestContent = `${ dangerTestContent }a`
beforeEach(() => {
cy.visitTestEditor()

View file

@ -12,7 +12,7 @@ describe('profile page', () => {
}, {
body: [
{
label: "cypress-App",
label: 'cypress-App',
created: 1601991518
}
]

View file

@ -21,7 +21,7 @@ describe('Toolbar Buttons', () => {
beforeEach(() => {
cy.codemirrorFill(testText)
cy.get('.CodeMirror-line > span')
.should("exist")
.should('exist')
.should('have.text', testText)
})
@ -35,42 +35,42 @@ describe('Toolbar Buttons', () => {
cy.get('.btn-toolbar [data-cy="format-bold"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `**${testText}**`)
.should('have.text', `**${ testText }**`)
})
it('should format as italic', () => {
cy.get('.btn-toolbar [data-cy="format-italic"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `*${testText}*`)
.should('have.text', `*${ testText }*`)
})
it('should format as underline', () => {
cy.get('.btn-toolbar [data-cy="format-underline"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `++${testText}++`)
.should('have.text', `++${ testText }++`)
})
it('should format as strikethrough', () => {
cy.get('.btn-toolbar [data-cy="format-strikethrough"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `~~${testText}~~`)
.should('have.text', `~~${ testText }~~`)
})
it('should format as subscript', () => {
cy.get('.btn-toolbar [data-cy="format-subscript"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `~${testText}~`)
.should('have.text', `~${ testText }~`)
})
it('should format as superscript', () => {
cy.get('.btn-toolbar [data-cy="format-superscript"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `^${testText}^`)
.should('have.text', `^${ testText }^`)
})
it('should format the line as code block', () => {
@ -88,14 +88,14 @@ describe('Toolbar Buttons', () => {
cy.get('.btn-toolbar [data-cy="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `[${testText}](https://)`)
.should('have.text', `[${ testText }](https://)`)
})
it('should format as image', () => {
cy.get('.btn-toolbar [data-cy="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![${testText}](https://)`)
.should('have.text', `![${ testText }](https://)`)
})
})
@ -103,11 +103,11 @@ describe('Toolbar Buttons', () => {
cy.get('.btn-toolbar [data-cy="format-heading"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `# ${testText}`)
.should('have.text', `# ${ testText }`)
cy.get('.fa-header')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `## ${testText}`)
.should('have.text', `## ${ testText }`)
})
it('should format the line as code', () => {
@ -125,58 +125,58 @@ describe('Toolbar Buttons', () => {
cy.get('.btn-toolbar [data-cy="format-block-quote"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `> ${testText}`)
.should('have.text', `> ${ testText }`)
cy.get('.btn-toolbar [data-cy="format-block-quote"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `> > ${testText}`)
.should('have.text', `> > ${ testText }`)
})
it('should format as unordered list', () => {
cy.get('.btn-toolbar [data-cy="format-unordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- ${testText}`)
.should('have.text', `- ${ testText }`)
cy.get('.btn-toolbar [data-cy="format-unordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- - ${testText}`)
.should('have.text', `- - ${ testText }`)
})
it('should format as ordered list', () => {
cy.get('.btn-toolbar [data-cy="format-ordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `1. ${testText}`)
.should('have.text', `1. ${ testText }`)
cy.get('.btn-toolbar [data-cy="format-ordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `1. 1. ${testText}`)
.should('have.text', `1. 1. ${ testText }`)
})
it('should format as check list', () => {
cy.get('.btn-toolbar [data-cy="format-check-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- [ ] ${testText}`)
.should('have.text', `- [ ] ${ testText }`)
cy.get('.btn-toolbar [data-cy="format-check-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- [ ] - [ ] ${testText}`)
.should('have.text', `- [ ] - [ ] ${ testText }`)
})
it('should insert links', () => {
cy.get('.btn-toolbar [data-cy="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${testText}[](https://)`)
.should('have.text', `${ testText }[](https://)`)
})
it('should insert an empty image link', () => {
cy.get('.btn-toolbar [data-cy="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${testText}![](https://)`)
.should('have.text', `${ testText }![](https://)`)
})
})
@ -184,7 +184,7 @@ describe('Toolbar Buttons', () => {
beforeEach(() => {
cy.codemirrorFill(testLink)
cy.get('.CodeMirror-line > span')
.should("exist")
.should('exist')
.should('have.text', testLink)
cy.get('@codeinput')
.type('{ctrl}a')
@ -194,14 +194,14 @@ describe('Toolbar Buttons', () => {
cy.get('.btn-toolbar [data-cy="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `[](${testLink})`)
.should('have.text', `[](${ testLink })`)
})
it('should format as image', () => {
cy.get('.btn-toolbar [data-cy="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${testLink})`)
.should('have.text', `![](${ testLink })`)
})
})

View file

@ -11,11 +11,12 @@ declare namespace Cypress {
* Custom command to check an external Link.
* @example cy.get(a#extern).checkExternalLink('http://example.com')
*/
checkExternalLink (url: string): Chainable<Element>
checkExternalLink(url: string): Chainable<Element>
}
}
Cypress.Commands.add('checkExternalLink', { prevSubject: 'element' }, ($element: JQuery, url: string) => {
cy.wrap($element).should('have.attr', 'href', url)
cy.wrap($element)
.should('have.attr', 'href', url)
.should('have.attr', 'target', '_blank')
})

View file

@ -50,9 +50,9 @@ beforeEach(() => {
sourceCodeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
issueTrackerUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
},
"iframeCommunication": {
"editorOrigin": "http://127.0.0.1:3001",
"rendererOrigin": "http://127.0.0.1:3001"
'iframeCommunication': {
'editorOrigin': 'http://127.0.0.1:3001',
'rendererOrigin': 'http://127.0.0.1:3001'
}
}
})

View file

@ -11,8 +11,9 @@ declare namespace Cypress {
* Custom command to fill an input field with text and trigger a change event.
* @example cy.get(input).fill('content')
*/
fill (value: string): Chainable<Element>
codemirrorFill (value: string): Chainable<Element>
fill(value: string): Chainable<Element>
codemirrorFill(value: string): Chainable<Element>
}
}
@ -20,17 +21,18 @@ Cypress.Commands.add('fill', {
prevSubject: 'element'
}, (subject, value) => {
return cy.wrap(subject)
.invoke('val', value)
.trigger('change', { force: true })
.invoke('val', value)
.trigger('change', { force: true })
})
Cypress.Commands.add('codemirrorFill', (content: string) => {
const line = content.split("\n").find(value => value !== '');
const line = content.split('\n')
.find(value => value !== '')
cy.get('.CodeMirror')
.click()
.get('textarea')
.fill(content)
if(line) {
if (line) {
cy.get('.CodeMirror')
.contains('.CodeMirror-line', line)
}

View file

@ -6,22 +6,22 @@
declare namespace Cypress {
interface Chainable {
getMarkdownRenderer (): Chainable<Element>
getMarkdownRenderer(): Chainable<Element>
getMarkdownBody (): Chainable<Element>
getMarkdownBody(): Chainable<Element>
}
}
Cypress.Commands.add('getMarkdownRenderer', () => {
return cy.get(`iframe[data-cy="documentIframe"]`)
.its('0.contentDocument')
.should('exist')
.its('body')
.should('not.be.undefined')
.then(cy.wrap.bind(cy))
.its('0.contentDocument')
.should('exist')
.its('body')
.should('not.be.undefined')
.then(cy.wrap.bind(cy))
})
Cypress.Commands.add('getMarkdownBody', () => {
return cy.getMarkdownRenderer()
.find('.markdown-body')
.find('.markdown-body')
})

View file

@ -11,11 +11,13 @@ declare namespace Cypress {
* Custom command to log the user out.
* @example cy.logout()
*/
logout (): Chainable<Window>
logout(): Chainable<Window>
}
}
Cypress.Commands.add('logout', () => {
cy.get('#dropdown-user').click()
cy.get('.fa-sign-out').click()
cy.get('#dropdown-user')
.click()
cy.get('.fa-sign-out')
.click()
})

View file

@ -6,28 +6,28 @@
declare namespace Cypress {
interface Chainable {
visitTestEditor (query?: string): Chainable<Cypress.AUTWindow>
visitTestEditor(query?: string): Chainable<Cypress.AUTWindow>
}
}
export const testNoteId = 'test'
Cypress.Commands.add('visitTestEditor', (query?: string) => {
return cy.visit(`/n/${testNoteId}${query ? `?${query}` : ''}`)
return cy.visit(`/n/${ testNoteId }${ query ? `?${ query }` : '' }`)
})
beforeEach(() => {
cy.intercept(`/api/v2/notes/${testNoteId}-get`, {
"id": "ABC123",
"alias": "banner",
"lastChange": {
"userId": "test",
"timestamp": 1600033920
cy.intercept(`/api/v2/notes/${ testNoteId }-get`, {
'id': 'ABC123',
'alias': 'banner',
'lastChange': {
'userId': 'test',
'timestamp': 1600033920
},
"viewCount": 0,
"createTime": 1600033920,
"content": "",
"authorship": [],
"preVersionTwoNote": true
'viewCount': 0,
'createTime': 1600033920,
'content': '',
'authorship': [],
'preVersionTwoNote': true
})
})

View file

@ -3,8 +3,15 @@
"strict": true,
"baseUrl": "../node_modules",
"target": "es6",
"lib": ["es6", "dom"],
"types": ["cypress" ,"cypress-commands", "cypress-file-upload"]
"lib": [
"es6",
"dom"
],
"types": [
"cypress",
"cypress-commands",
"cypress-file-upload"
]
},
"include": [
"**/*.ts"

View file

@ -25,7 +25,7 @@ export interface Note {
export const getNote = async (noteId: string): Promise<Note> => {
// The "-get" suffix is necessary, because in our mock api (filesystem) the note id might already be a folder.
// TODO: [mrdrogdrog] replace -get with actual api route as soon as api backend is ready.
const response = await fetch(getApiUrl() + `/notes/${noteId}-get`, {
const response = await fetch(getApiUrl() + `/notes/${ noteId }-get`, {
...defaultFetchConfig
})
expectResponseCode(response)
@ -33,7 +33,7 @@ export const getNote = async (noteId: string): Promise<Note> => {
}
export const deleteNote = async (noteId: string): Promise<void> => {
const response = await fetch(getApiUrl() + `/notes/${noteId}`, {
const response = await fetch(getApiUrl() + `/notes/${ noteId }`, {
...defaultFetchConfig,
method: 'DELETE'
})

View file

@ -11,11 +11,11 @@ import { Revision, RevisionListEntry } from './types'
const revisionCache = new Cache<string, Revision>(3600)
export const getRevision = async (noteId: string, timestamp: number): Promise<Revision> => {
const cacheKey = `${noteId}:${timestamp}`
const cacheKey = `${ noteId }:${ timestamp }`
if (revisionCache.has(cacheKey)) {
return revisionCache.get(cacheKey)
}
const response = await fetch(getApiUrl() + `/notes/${noteId}/revisions/${timestamp}`, {
const response = await fetch(getApiUrl() + `/notes/${ noteId }/revisions/${ timestamp }`, {
...defaultFetchConfig
})
expectResponseCode(response)
@ -26,7 +26,7 @@ export const getRevision = async (noteId: string, timestamp: number): Promise<Re
export const getAllRevisions = async (noteId: string): Promise<RevisionListEntry[]> => {
// TODO Change 'revisions-list' to 'revisions' as soon as the backend is ready to serve some data!
const response = await fetch(getApiUrl() + `/notes/${noteId}/revisions-list`, {
const response = await fetch(getApiUrl() + `/notes/${ noteId }/revisions-list`, {
...defaultFetchConfig
})
expectResponseCode(response)

View file

@ -8,7 +8,7 @@ import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils'
import { AccessToken, AccessTokenSecret } from './types'
export const getAccessTokenList = async (): Promise<AccessToken[]> => {
const response = await fetch(`${getApiUrl()}/tokens`, {
const response = await fetch(`${ getApiUrl() }/tokens`, {
...defaultFetchConfig
})
expectResponseCode(response)
@ -16,7 +16,7 @@ export const getAccessTokenList = async (): Promise<AccessToken[]> => {
}
export const postNewAccessToken = async (label: string): Promise<AccessToken & AccessTokenSecret> => {
const response = await fetch(`${getApiUrl()}/tokens`, {
const response = await fetch(`${ getApiUrl() }/tokens`, {
...defaultFetchConfig,
method: 'POST',
body: label
@ -26,7 +26,7 @@ export const postNewAccessToken = async (label: string): Promise<AccessToken & A
}
export const deleteAccessToken = async (timestamp: number): Promise<void> => {
const response = await fetch(`${getApiUrl()}/tokens/${timestamp}`, {
const response = await fetch(`${ getApiUrl() }/tokens/${ timestamp }`, {
...defaultFetchConfig,
method: 'DELETE'
})

View file

@ -14,7 +14,7 @@ export const getUserById = async (userid: string): Promise<UserResponse> => {
if (cache.has(userid)) {
return cache.get(userid)
}
const response = await fetch(`${getApiUrl()}/users/${userid}`, {
const response = await fetch(`${ getApiUrl() }/users/${ userid }`, {
...defaultFetchConfig
})
expectResponseCode(response)

View file

@ -24,6 +24,6 @@ export const getApiUrl = (): string => {
export const expectResponseCode = (response: Response, code = 200): void => {
if (response.status !== code) {
throw new Error(`response code is not ${code}`)
throw new Error(`response code is not ${ code }`)
}
}

View file

@ -6,32 +6,72 @@
.loader {
@keyframes animation-roll {
0% { transform: translateX(calc(-100vw / 2 - 100%)) rotateZ(0deg); }
100% { transform: translateX(calc(100vw / 2 + 100%)) rotateZ(720deg); }
0% {
transform: translateX(calc(-100vw / 2 - 100%)) rotateZ(0deg);
}
100% {
transform: translateX(calc(100vw / 2 + 100%)) rotateZ(720deg);
}
}
@keyframes animation-jump {
0% { transform: scale(1,1) translateY(0); }
10% { transform: scale(1.1,.9) translateY(0); }
30% { transform: scale(.9,1.1) translateY(-100px); }
50% { transform: scale(1.05,.95) translateY(0); }
57% { transform: scale(1,1) translateY(-7px); }
64% { transform: scale(1,1) translateY(0); }
100% { transform: scale(1,1) translateY(0); }
0% {
transform: scale(1, 1) translateY(0);
}
10% {
transform: scale(1.1, .9) translateY(0);
}
30% {
transform: scale(.9, 1.1) translateY(-100px);
}
50% {
transform: scale(1.05, .95) translateY(0);
}
57% {
transform: scale(1, 1) translateY(-7px);
}
64% {
transform: scale(1, 1) translateY(0);
}
100% {
transform: scale(1, 1) translateY(0);
}
}
@keyframes animation-shake {
0% { transform: translate(1px, 1px) rotate(0deg); }
10% { transform: translate(-1px, -2px) rotate(-1deg); }
20% { transform: translate(-3px, 0px) rotate(1deg); }
30% { transform: translate(3px, 2px) rotate(0deg); }
40% { transform: translate(1px, -1px) rotate(1deg); }
50% { transform: translate(-1px, 2px) rotate(-1deg); }
60% { transform: translate(-3px, 1px) rotate(0deg); }
70% { transform: translate(3px, 1px) rotate(-1deg); }
80% { transform: translate(-1px, -1px) rotate(1deg); }
90% { transform: translate(1px, 2px) rotate(0deg); }
100% { transform: translate(1px, -2px) rotate(-1deg); }
0% {
transform: translate(1px, 1px) rotate(0deg);
}
10% {
transform: translate(-1px, -2px) rotate(-1deg);
}
20% {
transform: translate(-3px, 0px) rotate(1deg);
}
30% {
transform: translate(3px, 2px) rotate(0deg);
}
40% {
transform: translate(1px, -1px) rotate(1deg);
}
50% {
transform: translate(-1px, 2px) rotate(-1deg);
}
60% {
transform: translate(-3px, 1px) rotate(0deg);
}
70% {
transform: translate(3px, 1px) rotate(-1deg);
}
80% {
transform: translate(-1px, -1px) rotate(1deg);
}
90% {
transform: translate(1px, 2px) rotate(0deg);
}
100% {
transform: translate(1px, -2px) rotate(-1deg);
}
}
.animation-roll {

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Suspense, useCallback, useEffect, useState } from 'react'
import { useFrontendBaseUrl } from '../../hooks/common/use-frontend-base-url'
@ -30,20 +30,21 @@ export const ApplicationLoader: React.FC = ({ children }) => {
useEffect(() => {
for (const task of initTasks) {
runTask(task.task).catch((reason: Error) => {
console.error(reason)
setFailedTitle(task.name)
})
runTask(task.task)
.catch((reason: Error) => {
console.error(reason)
setFailedTitle(task.name)
})
}
}, [initTasks, runTask])
const tasksAreRunning = doneTasks < initTasks.length || initTasks.length === 0
if (tasksAreRunning) {
return <LoadingScreen failedTitle={failedTitle}/>
return <LoadingScreen failedTitle={ failedTitle }/>
} else {
return <Suspense fallback={(<LoadingScreen/>)}>
{children}
return <Suspense fallback={ (<LoadingScreen/>) }>
{ children }
</Suspense>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Alert } from 'react-bootstrap'
@ -17,13 +17,13 @@ export const LoadingScreen: React.FC<LoadingScreenProps> = ({ failedTitle }) =>
return (
<div className="loader middle text-light overflow-hidden">
<div className="mb-3 text-light">
<span className={`d-block ${failedTitle ? 'animation-shake' : 'animation-jump'}`}>
<HedgeDocLogo size={HedgeDocLogoSize.BIG}/>
<span className={ `d-block ${ failedTitle ? 'animation-shake' : 'animation-jump' }` }>
<HedgeDocLogo size={ HedgeDocLogoSize.BIG }/>
</span>
</div>
<ShowIf condition={!!failedTitle}>
<Alert variant={'danger'}>
The task '{failedTitle}' failed.<br/>
<ShowIf condition={ !!failedTitle }>
<Alert variant={ 'danger' }>
The task '{ failedTitle }' failed.<br/>
For further information look into the browser console.
</Alert>
</ShowIf>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import equal from 'fast-deep-equal'
import React from 'react'
@ -21,18 +21,18 @@ export const Branding: React.FC<BrandingProps> = ({ inline = false, delimiter =
const showBranding = !!branding.name || !!branding.logo
return (
<ShowIf condition={showBranding}>
<ShowIf condition={delimiter}>
<strong className={`mx-1 ${inline ? 'inline-size' : 'regular-size'}`}>@</strong>
<ShowIf condition={ showBranding }>
<ShowIf condition={ delimiter }>
<strong className={ `mx-1 ${ inline ? 'inline-size' : 'regular-size' }` }>@</strong>
</ShowIf>
{
branding.logo
? <img
src={branding.logo}
alt={branding.name}
title={branding.name}
className={inline ? 'inline-size' : 'regular-size'}
/>
src={ branding.logo }
alt={ branding.name }
title={ branding.name }
className={ inline ? 'inline-size' : 'regular-size' }
/>
: branding.name
}
</ShowIf>

View file

@ -16,68 +16,88 @@ describe('Test caching functionality', () => {
it('initialize with right lifetime, no entry limit', () => {
const lifetime = 1000
const lifetimedCache = new Cache<string, string>(lifetime)
expect(lifetimedCache.entryLifetime).toEqual(lifetime)
expect(lifetimedCache.maxEntries).toEqual(0)
expect(lifetimedCache.entryLifetime)
.toEqual(lifetime)
expect(lifetimedCache.maxEntries)
.toEqual(0)
})
it('initialize with right lifetime, given entry limit', () => {
const lifetime = 1000
const maxEntries = 10
const limitedCache = new Cache<string, string>(lifetime, maxEntries)
expect(limitedCache.entryLifetime).toEqual(lifetime)
expect(limitedCache.maxEntries).toEqual(maxEntries)
expect(limitedCache.entryLifetime)
.toEqual(lifetime)
expect(limitedCache.maxEntries)
.toEqual(maxEntries)
})
it('entry exists after inserting', () => {
testCache.put('test', 123)
expect(testCache.has('test')).toBe(true)
expect(testCache.has('test'))
.toBe(true)
})
it('entry does not exist prior inserting', () => {
expect(testCache.has('test')).toBe(false)
expect(testCache.has('test'))
.toBe(false)
})
it('entry does expire', () => {
const shortLivingCache = new Cache<string, number>(2)
shortLivingCache.put('test', 123)
expect(shortLivingCache.has('test')).toBe(true)
expect(shortLivingCache.has('test'))
.toBe(true)
setTimeout(() => {
expect(shortLivingCache.has('test')).toBe(false)
expect(shortLivingCache.has('test'))
.toBe(false)
}, 2000)
})
it('entry value does not change', () => {
const testValue = Date.now()
testCache.put('test', testValue)
expect(testCache.get('test')).toEqual(testValue)
expect(testCache.get('test'))
.toEqual(testValue)
})
it('error is thrown on non-existent entry', () => {
const accessNonExistentEntry = () => {
testCache.get('test')
}
expect(accessNonExistentEntry).toThrow(Error)
expect(accessNonExistentEntry)
.toThrow(Error)
})
it('newer item replaces older item', () => {
testCache.put('test', 123)
testCache.put('test', 456)
expect(testCache.get('test')).toEqual(456)
expect(testCache.get('test'))
.toEqual(456)
})
it('entry limit is respected', () => {
const limitedCache = new Cache<string, number>(1000, 2)
limitedCache.put('first', 1)
expect(limitedCache.has('first')).toBe(true)
expect(limitedCache.has('second')).toBe(false)
expect(limitedCache.has('third')).toBe(false)
expect(limitedCache.has('first'))
.toBe(true)
expect(limitedCache.has('second'))
.toBe(false)
expect(limitedCache.has('third'))
.toBe(false)
limitedCache.put('second', 2)
expect(limitedCache.has('first')).toBe(true)
expect(limitedCache.has('second')).toBe(true)
expect(limitedCache.has('third')).toBe(false)
expect(limitedCache.has('first'))
.toBe(true)
expect(limitedCache.has('second'))
.toBe(true)
expect(limitedCache.has('third'))
.toBe(false)
limitedCache.put('third', 3)
expect(limitedCache.has('first')).toBe(false)
expect(limitedCache.has('second')).toBe(true)
expect(limitedCache.has('third')).toBe(true)
expect(limitedCache.has('first'))
.toBe(false)
expect(limitedCache.has('second'))
.toBe(true)
expect(limitedCache.has('third'))
.toBe(true)
})
})

View file

@ -10,12 +10,11 @@ export interface CacheEntry<T> {
}
export class Cache<K, V> {
private store = new Map<K, CacheEntry<V>>()
readonly entryLifetime: number
readonly maxEntries: number
private store = new Map<K, CacheEntry<V>>()
constructor (lifetime: number, maxEntries = 0) {
constructor(lifetime: number, maxEntries = 0) {
if (lifetime < 0) {
throw new Error('Cache entry lifetime can not be less than 0 seconds.')
}
@ -23,7 +22,7 @@ export class Cache<K, V> {
this.maxEntries = maxEntries
}
has (key: K): boolean {
has(key: K): boolean {
if (!this.store.has(key)) {
return false
}
@ -31,7 +30,7 @@ export class Cache<K, V> {
return (!!entry && entry.entryCreated >= (Date.now() - this.entryLifetime * 1000))
}
get (key: K): V {
get(key: K): V {
const entry = this.store.get(key)
if (!entry) {
throw new Error('This cache entry does not exist. Check with ".has()" before using ".get()".')
@ -39,9 +38,10 @@ export class Cache<K, V> {
return entry.data
}
put (key: K, value: V): void {
put(key: K, value: V): void {
if (this.maxEntries > 0 && this.store.size === this.maxEntries) {
this.store.delete(this.store.keys().next().value)
this.store.delete(this.store.keys()
.next().value)
}
this.store.set(key, {
entryCreated: Date.now(),

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { RefObject, useCallback, useEffect, useState } from 'react'
import { Overlay, Tooltip } from 'react-bootstrap'
@ -22,15 +22,20 @@ export const CopyOverlay: React.FC<CopyOverlayProps> = ({ content, clickComponen
const [tooltipId] = useState<string>(() => uuid())
const copyToClipboard = useCallback((content: string) => {
navigator.clipboard.writeText(content).then(() => {
setError(false)
}).catch(() => {
setError(true)
console.error("couldn't copy")
}).finally(() => {
setShowCopiedTooltip(true)
setTimeout(() => { setShowCopiedTooltip(false) }, 2000)
})
navigator.clipboard.writeText(content)
.then(() => {
setError(false)
})
.catch(() => {
setError(true)
console.error('couldn\'t copy')
})
.finally(() => {
setShowCopiedTooltip(true)
setTimeout(() => {
setShowCopiedTooltip(false)
}, 2000)
})
}, [])
useEffect(() => {
@ -46,17 +51,17 @@ export const CopyOverlay: React.FC<CopyOverlayProps> = ({ content, clickComponen
}, [clickComponent, copyToClipboard, content])
return (
<Overlay target={clickComponent} show={showCopiedTooltip} placement="top">
{(props) => (
<Tooltip id={`copied_${tooltipId}`} {...props}>
<ShowIf condition={error}>
<Trans i18nKey={'common.copyError'}/>
<Overlay target={ clickComponent } show={ showCopiedTooltip } placement="top">
{ (props) => (
<Tooltip id={ `copied_${ tooltipId }` } { ...props }>
<ShowIf condition={ error }>
<Trans i18nKey={ 'common.copyError' }/>
</ShowIf>
<ShowIf condition={!error}>
<Trans i18nKey={'common.successfullyCopied'}/>
<ShowIf condition={ !error }>
<Trans i18nKey={ 'common.successfullyCopied' }/>
</ShowIf>
</Tooltip>
)}
) }
</Overlay>
)
}

View file

@ -15,25 +15,25 @@ export interface CopyToClipboardButtonProps {
content: string
size?: 'sm' | 'lg'
variant?: Variant
"data-cy"?: string
'data-cy'?: string
}
export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
content,
size = 'sm',
variant = 'dark',
...props
}) => {
content,
size = 'sm',
variant = 'dark',
...props
}) => {
const { t } = useTranslation()
const button = useRef<HTMLButtonElement>(null)
return (
<Fragment>
<Button ref={button} size={size} variant={variant} title={t('renderer.highlightCode.copyCode')}
data-cy={props["data-cy"]}>
<Button ref={ button } size={ size } variant={ variant } title={ t('renderer.highlightCode.copyCode') }
data-cy={ props['data-cy'] }>
<ForkAwesomeIcon icon='files-o'/>
</Button>
<CopyOverlay content={content} clickComponent={button}/>
<CopyOverlay content={ content } clickComponent={ button }/>
</Fragment>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useRef } from 'react'
import { Button, FormControl, InputGroup } from 'react-bootstrap'
@ -25,9 +25,10 @@ export const CopyableField: React.FC<CopyableFieldProps> = ({ content, nativeSha
navigator.share({
text: content,
url: url
}).catch(err => {
console.error('Native sharing failed: ', err)
})
.catch(err => {
console.error('Native sharing failed: ', err)
})
}, [content, url])
const sharingSupported = typeof navigator.share === 'function'
@ -35,21 +36,21 @@ export const CopyableField: React.FC<CopyableFieldProps> = ({ content, nativeSha
return (
<Fragment>
<InputGroup className="my-3">
<FormControl readOnly={true} className={'text-center'} value={content} />
<FormControl readOnly={ true } className={ 'text-center' } value={ content }/>
<InputGroup.Append>
<Button variant="outline-secondary" ref={copyButton} title={'Copy'}>
<Button variant="outline-secondary" ref={ copyButton } title={ 'Copy' }>
<ForkAwesomeIcon icon='files-o'/>
</Button>
</InputGroup.Append>
<ShowIf condition={!!nativeShareButton && sharingSupported}>
<ShowIf condition={ !!nativeShareButton && sharingSupported }>
<InputGroup.Append>
<Button variant="outline-secondary" title={'Share'} onClick={doShareAction}>
<Button variant="outline-secondary" title={ 'Share' } onClick={ doShareAction }>
<ForkAwesomeIcon icon='share-alt'/>
</Button>
</InputGroup.Append>
</ShowIf>
</InputGroup>
<CopyOverlay content={content} clickComponent={copyButton}/>
<CopyOverlay content={ content } clickComponent={ copyButton }/>
</Fragment>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { IconName, IconSize } from './types'
@ -17,10 +17,10 @@ export interface ForkAwesomeIconProps {
export const ForkAwesomeIcon: React.FC<ForkAwesomeIconProps> = ({ icon, fixedWidth = false, size, className, stacked = false }) => {
const fixedWithClass = fixedWidth ? 'fa-fw' : ''
const sizeClass = size ? `-${size}` : (stacked ? '-1x' : '')
const sizeClass = size ? `-${ size }` : (stacked ? '-1x' : '')
const stackClass = stacked ? '-stack' : ''
const extraClasses = `${className ?? ''} ${sizeClass || stackClass ? `fa${stackClass}${sizeClass}` : ''}`
const extraClasses = `${ className ?? '' } ${ sizeClass || stackClass ? `fa${ stackClass }${ sizeClass }` : '' }`
return (
<i className={`fa ${fixedWithClass} fa-${icon} ${extraClasses}`}/>
<i className={ `fa ${ fixedWithClass } fa-${ icon } ${ extraClasses }` }/>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { ReactElement } from 'react'
import { ForkAwesomeIcon, ForkAwesomeIconProps } from './fork-awesome-icon'
@ -15,13 +15,13 @@ export interface ForkAwesomeStackProps {
export const ForkAwesomeStack: React.FC<ForkAwesomeStackProps> = ({ size, children }) => {
return (
<span className={`fa-stack ${size ? 'fa-' : ''}${size ?? ''}`}>
<span className={ `fa-stack ${ size ? 'fa-' : '' }${ size ?? '' }` }>
{
React.Children.map(children, (child) => {
if (!React.isValidElement<ForkAwesomeIconProps>(child)) {
return null
}
return <ForkAwesomeIcon {...child.props} stacked={true}/>
return <ForkAwesomeIcon { ...child.props } stacked={ true }/>
})
}
</span>

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { ReactComponent as LogoBwHorizontal } from './logo_text_bw_horizontal.svg'
@ -12,7 +12,7 @@ import { ReactComponent as LogoWbHorizontal } from './logo_text_wb_horizontal.sv
export enum HedgeDocLogoSize {
SMALL = 32,
MEDIUM = 64,
BIG= 256
BIG = 256
}
export interface HedgeDocLogoProps {
@ -29,11 +29,11 @@ export enum HedgeDocLogoType {
export const HedgeDocLogoWithText: React.FC<HedgeDocLogoProps> = ({ size = HedgeDocLogoSize.MEDIUM, logoType }) => {
switch (logoType) {
case HedgeDocLogoType.COLOR_VERTICAL:
return <LogoColorVertical className={'w-auto'} title={'HedgeDoc logo with text'} style={{ height: size }}/>
return <LogoColorVertical className={ 'w-auto' } title={ 'HedgeDoc logo with text' } style={ { height: size } }/>
case HedgeDocLogoType.BW_HORIZONTAL:
return <LogoBwHorizontal className={'w-auto'} title={'HedgeDoc logo with text'} style={{ height: size }}/>
return <LogoBwHorizontal className={ 'w-auto' } title={ 'HedgeDoc logo with text' } style={ { height: size } }/>
case HedgeDocLogoType.WB_HORIZONTAL:
return <LogoWbHorizontal className={'w-auto'} title={'HedgeDoc logo with text'} style={{ height: size }}/>
return <LogoWbHorizontal className={ 'w-auto' } title={ 'HedgeDoc logo with text' } style={ { height: size } }/>
default:
return null
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { ReactComponent as LogoColor } from './logo_color.svg'
@ -18,5 +18,5 @@ export interface HedgeDocLogoProps {
}
export const HedgeDocLogo: React.FC<HedgeDocLogoProps> = ({ size = HedgeDocLogoSize.MEDIUM }) => {
return <LogoColor className={'w-auto'} title={'HedgeDoc logo with text'} style={{ height: size }}/>
return <LogoColor className={ 'w-auto' } title={ 'HedgeDoc logo with text' } style={ { height: size } }/>
}

View file

@ -1 +1,29 @@
<svg clip-rule="evenodd" fill-rule="evenodd" height="1486" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 1486 1486" width="1486" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(200 -420 420 200 660 1340)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#fcca8c"/><stop offset="1" stop-color="#dca055"/></linearGradient><g fill-rule="nonzero" transform="translate(-142.756 -142.754)"><path d="m1553.66 961.083 75.25-75.258-75.25-75.246 56.61-90.096-90.1-56.616 35.15-100.446-100.44-35.142 11.91-105.746-105.74-11.912-11.92-105.75-105.74 11.912-35.15-100.445-100.43 35.145-56.63-90.1-90.107 56.617-75.242-75.246-75.242 75.25-90.104-56.612-56.612 90.096-100.442-35.15-35.142 100.441-105.75-11.916-11.912 105.741-105.742 11.913 11.917 105.741-100.454 35.15 35.145 100.438-90.1 56.612 56.621 90.117-75.258 75.25 75.25 75.258-56.613 90.107 90.1 56.61-35.145 100.44 100.45 35.15-11.913 105.73 105.742 11.92 11.912 105.74 105.75-11.91 35.142 100.43 100.437-35.13 56.617 90.1 90.096-56.62 75.25 74.55 75.25-74.53 90.099 56.61 56.61-90.11 100.45 35.15 35.14-100.44 105.75 11.91 11.91-105.75 105.75-11.91-11.92-105.74 100.45-35.15-35.15-100.45 90.1-56.61z" fill="#b51f08"/><path d="m1401.3 1004.78c0-145.437-117.9-263.341-263.34-263.341-72.24 0-137.68 29.112-185.252 76.225l-.033-.034-67.096 67.1-54.862-54.862c-48.267-55.067-119.071-89.879-198.042-89.879-145.442 0-263.342 117.904-263.342 263.341 0 76 32.23 144.43 83.721 192.49l432.779 432.55 423.217-423.49c52.88-49.77 92.25-120 92.25-200.1" fill="#fcca8c"/><path d="m885.579 884.73-54.862-54.862c-48.267-55.067-119.071-89.879-198.042-89.879-145.442 0-263.342 117.904-263.342 263.341 0 76 32.23 144.43 83.721 192.49l432.779 432.55" fill="url(#a)"/></g><path d="m743.077 1485.616-.254-743.64" fill="none"/><g fill-rule="nonzero" transform="translate(-142.756 -142.754)"><path d="m961.011 1553.59c-19.279-19.35-45.917-31.31-75.325-31.31-29.417 0-56.046 11.96-75.329 31.31.308 41.34 33.908 74.76 75.325 74.76 41.416 0 75.02-33.43 75.329-74.76" fill="#010007"/><path d="m797.707 1098.22c0 31.8-25.767 57.57-57.571 57.57-31.787 0-57.558-25.77-57.558-57.57s25.771-57.57 57.558-57.57c31.804 0 57.571 25.77 57.571 57.57" fill="#010007"/><path d="m777.962 1089.59c0 8.82-7.146 15.94-15.95 15.94-8.808 0-15.958-7.12-15.958-15.94 0-8.81 7.15-15.96 15.958-15.96 8.804 0 15.95 7.15 15.95 15.96" fill="#fffffa"/><path d="m1089.65 1098.22c0 31.8-25.77 57.57-57.57 57.57-31.79 0-57.567-25.77-57.567-57.57s25.777-57.57 57.567-57.57c31.8 0 57.57 25.77 57.57 57.57" fill="#010007"/><path d="m1069.9 1089.59c0 8.82-7.15 15.94-15.95 15.94-8.81 0-15.96-7.12-15.96-15.94 0-8.81 7.15-15.96 15.96-15.96 8.8 0 15.95 7.15 15.95 15.96" fill="#fffffa"/></g></svg>
<svg clip-rule="evenodd" fill-rule="evenodd" height="1486" stroke-linejoin="round" stroke-miterlimit="2"
viewBox="0 0 1486 1486" width="1486" xmlns="http://www.w3.org/2000/svg">
<linearGradient id="a" gradientTransform="matrix(200 -420 420 200 660 1340)" gradientUnits="userSpaceOnUse" x1="0"
x2="1" y1="0" y2="0">
<stop offset="0" stop-color="#fcca8c"/>
<stop offset="1" stop-color="#dca055"/>
</linearGradient>
<g fill-rule="nonzero" transform="translate(-142.756 -142.754)">
<path d="m1553.66 961.083 75.25-75.258-75.25-75.246 56.61-90.096-90.1-56.616 35.15-100.446-100.44-35.142 11.91-105.746-105.74-11.912-11.92-105.75-105.74 11.912-35.15-100.445-100.43 35.145-56.63-90.1-90.107 56.617-75.242-75.246-75.242 75.25-90.104-56.612-56.612 90.096-100.442-35.15-35.142 100.441-105.75-11.916-11.912 105.741-105.742 11.913 11.917 105.741-100.454 35.15 35.145 100.438-90.1 56.612 56.621 90.117-75.258 75.25 75.25 75.258-56.613 90.107 90.1 56.61-35.145 100.44 100.45 35.15-11.913 105.73 105.742 11.92 11.912 105.74 105.75-11.91 35.142 100.43 100.437-35.13 56.617 90.1 90.096-56.62 75.25 74.55 75.25-74.53 90.099 56.61 56.61-90.11 100.45 35.15 35.14-100.44 105.75 11.91 11.91-105.75 105.75-11.91-11.92-105.74 100.45-35.15-35.15-100.45 90.1-56.61z"
fill="#b51f08"/>
<path d="m1401.3 1004.78c0-145.437-117.9-263.341-263.34-263.341-72.24 0-137.68 29.112-185.252 76.225l-.033-.034-67.096 67.1-54.862-54.862c-48.267-55.067-119.071-89.879-198.042-89.879-145.442 0-263.342 117.904-263.342 263.341 0 76 32.23 144.43 83.721 192.49l432.779 432.55 423.217-423.49c52.88-49.77 92.25-120 92.25-200.1"
fill="#fcca8c"/>
<path d="m885.579 884.73-54.862-54.862c-48.267-55.067-119.071-89.879-198.042-89.879-145.442 0-263.342 117.904-263.342 263.341 0 76 32.23 144.43 83.721 192.49l432.779 432.55"
fill="url(#a)"/>
</g>
<path d="m743.077 1485.616-.254-743.64" fill="none"/>
<g fill-rule="nonzero" transform="translate(-142.756 -142.754)">
<path d="m961.011 1553.59c-19.279-19.35-45.917-31.31-75.325-31.31-29.417 0-56.046 11.96-75.329 31.31.308 41.34 33.908 74.76 75.325 74.76 41.416 0 75.02-33.43 75.329-74.76"
fill="#010007"/>
<path d="m797.707 1098.22c0 31.8-25.767 57.57-57.571 57.57-31.787 0-57.558-25.77-57.558-57.57s25.771-57.57 57.558-57.57c31.804 0 57.571 25.77 57.571 57.57"
fill="#010007"/>
<path d="m777.962 1089.59c0 8.82-7.146 15.94-15.95 15.94-8.808 0-15.958-7.12-15.958-15.94 0-8.81 7.15-15.96 15.958-15.96 8.804 0 15.95 7.15 15.95 15.96"
fill="#fffffa"/>
<path d="m1089.65 1098.22c0 31.8-25.77 57.57-57.57 57.57-31.79 0-57.567-25.77-57.567-57.57s25.777-57.57 57.567-57.57c31.8 0 57.57 25.77 57.57 57.57"
fill="#010007"/>
<path d="m1069.9 1089.59c0 8.82-7.15 15.94-15.95 15.94-8.81 0-15.96-7.12-15.96-15.94 0-8.81 7.15-15.96 15.96-15.96 8.8 0 15.95 7.15 15.95 15.96"
fill="#fffffa"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -1,9 +1,9 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
import hljs from 'highlight.js/lib/core';
SPDX-License-Identifier: AGPL-3.0-only
*/
import hljs from 'highlight.js/lib/core'
import abnf from 'highlight.js/lib/languages/abnf'
import accesslog from 'highlight.js/lib/languages/accesslog'
import actionscript from 'highlight.js/lib/languages/actionscript'
@ -194,196 +194,196 @@ import xl from 'highlight.js/lib/languages/xl'
import xquery from 'highlight.js/lib/languages/xquery'
import zephir from 'highlight.js/lib/languages/zephir'
hljs.registerLanguage('abnf', abnf);
hljs.registerLanguage('accesslog', accesslog);
hljs.registerLanguage('actionscript', actionscript);
hljs.registerLanguage('ada', ada);
hljs.registerLanguage('angelscript', angelscript);
hljs.registerLanguage('apache', apache);
hljs.registerLanguage('applescript', applescript);
hljs.registerLanguage('arcade', arcade);
hljs.registerLanguage('arduino', arduino);
hljs.registerLanguage('armasm', armasm);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('asciidoc', asciidoc);
hljs.registerLanguage('aspectj', aspectj);
hljs.registerLanguage('autohotkey', autohotkey);
hljs.registerLanguage('autoit', autoit);
hljs.registerLanguage('avrasm', avrasm);
hljs.registerLanguage('awk', awk);
hljs.registerLanguage('axapta', axapta);
hljs.registerLanguage('bash', bash);
hljs.registerLanguage('basic', basic);
hljs.registerLanguage('bnf', bnf);
hljs.registerLanguage('brainfuck', brainfuck);
hljs.registerLanguage('c-like', c_like);
hljs.registerLanguage('c', c);
hljs.registerLanguage('cal', cal);
hljs.registerLanguage('capnproto', capnproto);
hljs.registerLanguage('ceylon', ceylon);
hljs.registerLanguage('clean', clean);
hljs.registerLanguage('clojure', clojure);
hljs.registerLanguage('clojure-repl', clojure_repl);
hljs.registerLanguage('cmake', cmake);
hljs.registerLanguage('coffeescript', coffeescript);
hljs.registerLanguage('coq', coq);
hljs.registerLanguage('cos', cos);
hljs.registerLanguage('cpp', cpp);
hljs.registerLanguage('crmsh', crmsh);
hljs.registerLanguage('crystal', crystal);
hljs.registerLanguage('csharp', csharp);
hljs.registerLanguage('csp', csp);
hljs.registerLanguage('css', css);
hljs.registerLanguage('d', d);
hljs.registerLanguage('markdown', markdown);
hljs.registerLanguage('dart', dart);
hljs.registerLanguage('delphi', delphi);
hljs.registerLanguage('diff', diff);
hljs.registerLanguage('django', django);
hljs.registerLanguage('dns', dns);
hljs.registerLanguage('dockerfile', dockerfile);
hljs.registerLanguage('dos', dos);
hljs.registerLanguage('dsconfig', dsconfig);
hljs.registerLanguage('dts', dts);
hljs.registerLanguage('dust', dust);
hljs.registerLanguage('ebnf', ebnf);
hljs.registerLanguage('elixir', elixir);
hljs.registerLanguage('elm', elm);
hljs.registerLanguage('ruby', ruby);
hljs.registerLanguage('erb', erb);
hljs.registerLanguage('erlang-repl', erlang_repl);
hljs.registerLanguage('erlang', erlang);
hljs.registerLanguage('excel', excel);
hljs.registerLanguage('fix', fix);
hljs.registerLanguage('flix', flix);
hljs.registerLanguage('fortran', fortran);
hljs.registerLanguage('fsharp', fsharp);
hljs.registerLanguage('gams', gams);
hljs.registerLanguage('gauss', gauss);
hljs.registerLanguage('gcode', gcode);
hljs.registerLanguage('gherkin', gherkin);
hljs.registerLanguage('glsl', glsl);
hljs.registerLanguage('gml', gml);
hljs.registerLanguage('go', go);
hljs.registerLanguage('golo', golo);
hljs.registerLanguage('gradle', gradle);
hljs.registerLanguage('groovy', groovy);
hljs.registerLanguage('haml', haml);
hljs.registerLanguage('handlebars', handlebars);
hljs.registerLanguage('haskell', haskell);
hljs.registerLanguage('haxe', haxe);
hljs.registerLanguage('hsp', hsp);
hljs.registerLanguage('html', xml);
hljs.registerLanguage('htmlbars', htmlbars);
hljs.registerLanguage('http', http);
hljs.registerLanguage('hy', hy);
hljs.registerLanguage('inform7', inform7);
hljs.registerLanguage('ini', ini);
hljs.registerLanguage('irpf90', irpf90);
hljs.registerLanguage('isbl', isbl);
hljs.registerLanguage('java', java);
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('jboss-cli', jboss_cli);
hljs.registerLanguage('js', javascript);
hljs.registerLanguage('json', json);
hljs.registerLanguage('julia', julia);
hljs.registerLanguage('julia-repl', julia_repl);
hljs.registerLanguage('kotlin', kotlin);
hljs.registerLanguage('lasso', lasso);
hljs.registerLanguage('latex', latex);
hljs.registerLanguage('ldif', ldif);
hljs.registerLanguage('leaf', leaf);
hljs.registerLanguage('less', less);
hljs.registerLanguage('lisp', lisp);
hljs.registerLanguage('livecodeserver', livecodeserver);
hljs.registerLanguage('livescript', livescript);
hljs.registerLanguage('llvm', llvm);
hljs.registerLanguage('lsl', lsl);
hljs.registerLanguage('lua', lua);
hljs.registerLanguage('makefile', makefile);
hljs.registerLanguage('mathematica', mathematica);
hljs.registerLanguage('matlab', matlab);
hljs.registerLanguage('maxima', maxima);
hljs.registerLanguage('mel', mel);
hljs.registerLanguage('mercury', mercury);
hljs.registerLanguage('mipsasm', mipsasm);
hljs.registerLanguage('mizar', mizar);
hljs.registerLanguage('perl', perl);
hljs.registerLanguage('mojolicious', mojolicious);
hljs.registerLanguage('monkey', monkey);
hljs.registerLanguage('moonscript', moonscript);
hljs.registerLanguage('n1ql', n1ql);
hljs.registerLanguage('nginx', nginx);
hljs.registerLanguage('nim', nim);
hljs.registerLanguage('nix', nix);
hljs.registerLanguage('node-repl', node_repl);
hljs.registerLanguage('nsis', nsis);
hljs.registerLanguage('objectivec', objectivec);
hljs.registerLanguage('ocaml', ocaml);
hljs.registerLanguage('openscad', openscad);
hljs.registerLanguage('oxygene', oxygene);
hljs.registerLanguage('parser3', parser3);
hljs.registerLanguage('pf', pf);
hljs.registerLanguage('pgsql', pgsql);
hljs.registerLanguage('php', php);
hljs.registerLanguage('php-template', php_template);
hljs.registerLanguage('plaintext', plaintext);
hljs.registerLanguage('pony', pony);
hljs.registerLanguage('powershell', powershell);
hljs.registerLanguage('processing', processing);
hljs.registerLanguage('profile', profile);
hljs.registerLanguage('prolog', prolog);
hljs.registerLanguage('properties', properties);
hljs.registerLanguage('protobuf', protobuf);
hljs.registerLanguage('puppet', puppet);
hljs.registerLanguage('purebasic', purebasic);
hljs.registerLanguage('python', python);
hljs.registerLanguage('python-repl', python_repl);
hljs.registerLanguage('q', q);
hljs.registerLanguage('qml', qml);
hljs.registerLanguage('r', r);
hljs.registerLanguage('reasonml', reasonml);
hljs.registerLanguage('rib', rib);
hljs.registerLanguage('roboconf', roboconf);
hljs.registerLanguage('routeros', routeros);
hljs.registerLanguage('rsl', rsl);
hljs.registerLanguage('ruleslanguage', ruleslanguage);
hljs.registerLanguage('rust', rust);
hljs.registerLanguage('sas', sas);
hljs.registerLanguage('scala', scala);
hljs.registerLanguage('scheme', scheme);
hljs.registerLanguage('scilab', scilab);
hljs.registerLanguage('scss', scss);
hljs.registerLanguage('shell', shell);
hljs.registerLanguage('smali', smali);
hljs.registerLanguage('smalltalk', smalltalk);
hljs.registerLanguage('sml', sml);
hljs.registerLanguage('sqf', sqf);
hljs.registerLanguage('sql', sql);
hljs.registerLanguage('stan', stan);
hljs.registerLanguage('stata', stata);
hljs.registerLanguage('step21', step21);
hljs.registerLanguage('stylus', stylus);
hljs.registerLanguage('subunit', subunit);
hljs.registerLanguage('swift', swift);
hljs.registerLanguage('taggerscript', taggerscript);
hljs.registerLanguage('yaml', yaml);
hljs.registerLanguage('tap', tap);
hljs.registerLanguage('tcl', tcl);
hljs.registerLanguage('thrift', thrift);
hljs.registerLanguage('tp', tp);
hljs.registerLanguage('twig', twig);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('vala', vala);
hljs.registerLanguage('vbnet', vbnet);
hljs.registerLanguage('vbscript', vbscript);
hljs.registerLanguage('vbscript-html', vbscript_html);
hljs.registerLanguage('verilog', verilog);
hljs.registerLanguage('vhdl', vhdl);
hljs.registerLanguage('vim', vim);
hljs.registerLanguage('x86asm', x86asm);
hljs.registerLanguage('xl', xl);
hljs.registerLanguage('xquery', xquery);
hljs.registerLanguage('zephir', zephir);
hljs.registerLanguage('abnf', abnf)
hljs.registerLanguage('accesslog', accesslog)
hljs.registerLanguage('actionscript', actionscript)
hljs.registerLanguage('ada', ada)
hljs.registerLanguage('angelscript', angelscript)
hljs.registerLanguage('apache', apache)
hljs.registerLanguage('applescript', applescript)
hljs.registerLanguage('arcade', arcade)
hljs.registerLanguage('arduino', arduino)
hljs.registerLanguage('armasm', armasm)
hljs.registerLanguage('xml', xml)
hljs.registerLanguage('asciidoc', asciidoc)
hljs.registerLanguage('aspectj', aspectj)
hljs.registerLanguage('autohotkey', autohotkey)
hljs.registerLanguage('autoit', autoit)
hljs.registerLanguage('avrasm', avrasm)
hljs.registerLanguage('awk', awk)
hljs.registerLanguage('axapta', axapta)
hljs.registerLanguage('bash', bash)
hljs.registerLanguage('basic', basic)
hljs.registerLanguage('bnf', bnf)
hljs.registerLanguage('brainfuck', brainfuck)
hljs.registerLanguage('c-like', c_like)
hljs.registerLanguage('c', c)
hljs.registerLanguage('cal', cal)
hljs.registerLanguage('capnproto', capnproto)
hljs.registerLanguage('ceylon', ceylon)
hljs.registerLanguage('clean', clean)
hljs.registerLanguage('clojure', clojure)
hljs.registerLanguage('clojure-repl', clojure_repl)
hljs.registerLanguage('cmake', cmake)
hljs.registerLanguage('coffeescript', coffeescript)
hljs.registerLanguage('coq', coq)
hljs.registerLanguage('cos', cos)
hljs.registerLanguage('cpp', cpp)
hljs.registerLanguage('crmsh', crmsh)
hljs.registerLanguage('crystal', crystal)
hljs.registerLanguage('csharp', csharp)
hljs.registerLanguage('csp', csp)
hljs.registerLanguage('css', css)
hljs.registerLanguage('d', d)
hljs.registerLanguage('markdown', markdown)
hljs.registerLanguage('dart', dart)
hljs.registerLanguage('delphi', delphi)
hljs.registerLanguage('diff', diff)
hljs.registerLanguage('django', django)
hljs.registerLanguage('dns', dns)
hljs.registerLanguage('dockerfile', dockerfile)
hljs.registerLanguage('dos', dos)
hljs.registerLanguage('dsconfig', dsconfig)
hljs.registerLanguage('dts', dts)
hljs.registerLanguage('dust', dust)
hljs.registerLanguage('ebnf', ebnf)
hljs.registerLanguage('elixir', elixir)
hljs.registerLanguage('elm', elm)
hljs.registerLanguage('ruby', ruby)
hljs.registerLanguage('erb', erb)
hljs.registerLanguage('erlang-repl', erlang_repl)
hljs.registerLanguage('erlang', erlang)
hljs.registerLanguage('excel', excel)
hljs.registerLanguage('fix', fix)
hljs.registerLanguage('flix', flix)
hljs.registerLanguage('fortran', fortran)
hljs.registerLanguage('fsharp', fsharp)
hljs.registerLanguage('gams', gams)
hljs.registerLanguage('gauss', gauss)
hljs.registerLanguage('gcode', gcode)
hljs.registerLanguage('gherkin', gherkin)
hljs.registerLanguage('glsl', glsl)
hljs.registerLanguage('gml', gml)
hljs.registerLanguage('go', go)
hljs.registerLanguage('golo', golo)
hljs.registerLanguage('gradle', gradle)
hljs.registerLanguage('groovy', groovy)
hljs.registerLanguage('haml', haml)
hljs.registerLanguage('handlebars', handlebars)
hljs.registerLanguage('haskell', haskell)
hljs.registerLanguage('haxe', haxe)
hljs.registerLanguage('hsp', hsp)
hljs.registerLanguage('html', xml)
hljs.registerLanguage('htmlbars', htmlbars)
hljs.registerLanguage('http', http)
hljs.registerLanguage('hy', hy)
hljs.registerLanguage('inform7', inform7)
hljs.registerLanguage('ini', ini)
hljs.registerLanguage('irpf90', irpf90)
hljs.registerLanguage('isbl', isbl)
hljs.registerLanguage('java', java)
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('jboss-cli', jboss_cli)
hljs.registerLanguage('js', javascript)
hljs.registerLanguage('json', json)
hljs.registerLanguage('julia', julia)
hljs.registerLanguage('julia-repl', julia_repl)
hljs.registerLanguage('kotlin', kotlin)
hljs.registerLanguage('lasso', lasso)
hljs.registerLanguage('latex', latex)
hljs.registerLanguage('ldif', ldif)
hljs.registerLanguage('leaf', leaf)
hljs.registerLanguage('less', less)
hljs.registerLanguage('lisp', lisp)
hljs.registerLanguage('livecodeserver', livecodeserver)
hljs.registerLanguage('livescript', livescript)
hljs.registerLanguage('llvm', llvm)
hljs.registerLanguage('lsl', lsl)
hljs.registerLanguage('lua', lua)
hljs.registerLanguage('makefile', makefile)
hljs.registerLanguage('mathematica', mathematica)
hljs.registerLanguage('matlab', matlab)
hljs.registerLanguage('maxima', maxima)
hljs.registerLanguage('mel', mel)
hljs.registerLanguage('mercury', mercury)
hljs.registerLanguage('mipsasm', mipsasm)
hljs.registerLanguage('mizar', mizar)
hljs.registerLanguage('perl', perl)
hljs.registerLanguage('mojolicious', mojolicious)
hljs.registerLanguage('monkey', monkey)
hljs.registerLanguage('moonscript', moonscript)
hljs.registerLanguage('n1ql', n1ql)
hljs.registerLanguage('nginx', nginx)
hljs.registerLanguage('nim', nim)
hljs.registerLanguage('nix', nix)
hljs.registerLanguage('node-repl', node_repl)
hljs.registerLanguage('nsis', nsis)
hljs.registerLanguage('objectivec', objectivec)
hljs.registerLanguage('ocaml', ocaml)
hljs.registerLanguage('openscad', openscad)
hljs.registerLanguage('oxygene', oxygene)
hljs.registerLanguage('parser3', parser3)
hljs.registerLanguage('pf', pf)
hljs.registerLanguage('pgsql', pgsql)
hljs.registerLanguage('php', php)
hljs.registerLanguage('php-template', php_template)
hljs.registerLanguage('plaintext', plaintext)
hljs.registerLanguage('pony', pony)
hljs.registerLanguage('powershell', powershell)
hljs.registerLanguage('processing', processing)
hljs.registerLanguage('profile', profile)
hljs.registerLanguage('prolog', prolog)
hljs.registerLanguage('properties', properties)
hljs.registerLanguage('protobuf', protobuf)
hljs.registerLanguage('puppet', puppet)
hljs.registerLanguage('purebasic', purebasic)
hljs.registerLanguage('python', python)
hljs.registerLanguage('python-repl', python_repl)
hljs.registerLanguage('q', q)
hljs.registerLanguage('qml', qml)
hljs.registerLanguage('r', r)
hljs.registerLanguage('reasonml', reasonml)
hljs.registerLanguage('rib', rib)
hljs.registerLanguage('roboconf', roboconf)
hljs.registerLanguage('routeros', routeros)
hljs.registerLanguage('rsl', rsl)
hljs.registerLanguage('ruleslanguage', ruleslanguage)
hljs.registerLanguage('rust', rust)
hljs.registerLanguage('sas', sas)
hljs.registerLanguage('scala', scala)
hljs.registerLanguage('scheme', scheme)
hljs.registerLanguage('scilab', scilab)
hljs.registerLanguage('scss', scss)
hljs.registerLanguage('shell', shell)
hljs.registerLanguage('smali', smali)
hljs.registerLanguage('smalltalk', smalltalk)
hljs.registerLanguage('sml', sml)
hljs.registerLanguage('sqf', sqf)
hljs.registerLanguage('sql', sql)
hljs.registerLanguage('stan', stan)
hljs.registerLanguage('stata', stata)
hljs.registerLanguage('step21', step21)
hljs.registerLanguage('stylus', stylus)
hljs.registerLanguage('subunit', subunit)
hljs.registerLanguage('swift', swift)
hljs.registerLanguage('taggerscript', taggerscript)
hljs.registerLanguage('yaml', yaml)
hljs.registerLanguage('tap', tap)
hljs.registerLanguage('tcl', tcl)
hljs.registerLanguage('thrift', thrift)
hljs.registerLanguage('tp', tp)
hljs.registerLanguage('twig', twig)
hljs.registerLanguage('typescript', typescript)
hljs.registerLanguage('vala', vala)
hljs.registerLanguage('vbnet', vbnet)
hljs.registerLanguage('vbscript', vbscript)
hljs.registerLanguage('vbscript-html', vbscript_html)
hljs.registerLanguage('verilog', verilog)
hljs.registerLanguage('vhdl', vhdl)
hljs.registerLanguage('vim', vim)
hljs.registerLanguage('x86asm', x86asm)
hljs.registerLanguage('xl', xl)
hljs.registerLanguage('xquery', xquery)
hljs.registerLanguage('zephir', zephir)
export default hljs;
export default hljs

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Button, ButtonProps } from 'react-bootstrap'
@ -19,13 +19,14 @@ export interface IconButtonProps extends ButtonProps {
export const IconButton: React.FC<IconButtonProps> = ({ icon, children, border = false, ...props }) => {
return (
<Button {...props} className={`btn-icon p-0 d-inline-flex align-items-stretch ${border ? 'with-border' : ''}`}>
<Button { ...props }
className={ `btn-icon p-0 d-inline-flex align-items-stretch ${ border ? 'with-border' : '' }` }>
<span className="icon-part d-flex align-items-center">
<ForkAwesomeIcon icon={icon} className={'icon'}/>
<ForkAwesomeIcon icon={ icon } className={ 'icon' }/>
</span>
<ShowIf condition={!!children}>
<ShowIf condition={ !!children }>
<span className="text-part d-flex align-items-center">
{children}
{ children }
</span>
</ShowIf>
</Button>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Trans } from 'react-i18next'
@ -14,8 +14,8 @@ export interface TranslatedIconButtonProps extends IconButtonProps {
export const TranslatedIconButton: React.FC<TranslatedIconButtonProps> = ({ i18nKey, ...props }) => {
return (
<IconButton {...props}>
<Trans i18nKey={i18nKey}/>
<IconButton { ...props }>
<Trans i18nKey={ i18nKey }/>
</IconButton>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon'
@ -12,18 +12,18 @@ import { LinkWithTextProps } from './types'
export const ExternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = 'text-light', title }) => {
return (
<a href={href}
target="_blank"
rel="noopener noreferrer"
id={id}
className={className}
title={title}
dir='auto'
<a href={ href }
target="_blank"
rel="noopener noreferrer"
id={ id }
className={ className }
title={ title }
dir='auto'
>
<ShowIf condition={!!icon}>
<ForkAwesomeIcon icon={icon as IconName} fixedWidth={true}/>&nbsp;
<ShowIf condition={ !!icon }>
<ForkAwesomeIcon icon={ icon as IconName } fixedWidth={ true }/>&nbsp;
</ShowIf>
{text}
{ text }
</a>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Link } from 'react-router-dom'
@ -13,15 +13,15 @@ import { LinkWithTextProps } from './types'
export const InternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = 'text-light', title }) => {
return (
<Link to={href}
className={className}
id={id}
title={title}
<Link to={ href }
className={ className }
id={ id }
title={ title }
>
<ShowIf condition={!!icon}>
<ForkAwesomeIcon icon={icon as IconName} fixedWidth={true}/>&nbsp;
<ShowIf condition={ !!icon }>
<ForkAwesomeIcon icon={ icon as IconName } fixedWidth={ true }/>&nbsp;
</ShowIf>
{text}
{ text }
</Link>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { useTranslation } from 'react-i18next'
@ -12,6 +12,6 @@ import { TranslatedLinkProps } from './types'
export const TranslatedExternalLink: React.FC<TranslatedLinkProps> = ({ i18nKey, i18nOption, ...props }) => {
const { t } = useTranslation()
return (
<ExternalLink text={t(i18nKey, i18nOption)} {...props}/>
<ExternalLink text={ t(i18nKey, i18nOption) } { ...props }/>
)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { useTranslation } from 'react-i18next'
@ -12,6 +12,6 @@ import { TranslatedLinkProps } from './types'
export const TranslatedInternalLink: React.FC<TranslatedLinkProps> = ({ i18nKey, i18nOption, ...props }) => {
const { t } = useTranslation()
return (
<InternalLink text={t(i18nKey, i18nOption)} {...props}/>
<InternalLink text={ t(i18nKey, i18nOption) } { ...props }/>
)
}

View file

@ -19,7 +19,7 @@ export interface LinkWithTextProps extends GeneralLinkProp {
text: string
}
export interface TranslatedLinkProps extends GeneralLinkProp{
export interface TranslatedLinkProps extends GeneralLinkProp {
i18nKey: string
i18nOption?: (TOptionsBase & StringMap) | string
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Button } from 'react-bootstrap'
@ -16,7 +16,7 @@ export interface LockButtonProps {
export const LockButton: React.FC<LockButtonProps> = ({ locked, onLockedChanged, title }) => {
return (
<Button variant='dark' size='sm' onClick={() => onLockedChanged(!locked)} title={title}>
<Button variant='dark' size='sm' onClick={ () => onLockedChanged(!locked) } title={ title }>
{ locked
? <ForkAwesomeIcon icon='lock'/>
: <ForkAwesomeIcon icon='unlock'/>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Modal } from 'react-bootstrap'
@ -26,15 +26,16 @@ export const CommonModal: React.FC<CommonModalProps> = ({ show, onHide, titleI18
useTranslation()
return (
<Modal data-cy={'limitReachedModal'} show={show} onHide={onHide} animation={true} dialogClassName={`text-dark ${additionalClasses ?? ''}`} size={size}>
<Modal.Header closeButton={!!closeButton}>
<Modal data-cy={ 'limitReachedModal' } show={ show } onHide={ onHide } animation={ true }
dialogClassName={ `text-dark ${ additionalClasses ?? '' }` } size={ size }>
<Modal.Header closeButton={ !!closeButton }>
<Modal.Title>
<ShowIf condition={!!icon}>
<ForkAwesomeIcon icon={icon as IconName}/>&nbsp;
<ShowIf condition={ !!icon }>
<ForkAwesomeIcon icon={ icon as IconName }/>&nbsp;
</ShowIf>
{ titleI18nKey
? <Trans i18nKey={titleI18nKey}/>
: <span>{title}</span>
? <Trans i18nKey={ titleI18nKey }/>
: <span>{ title }</span>
}
</Modal.Title>
</Modal.Header>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Button, Modal } from 'react-bootstrap'
@ -18,13 +18,13 @@ export const DeletionModal: React.FC<DeletionModalProps> = ({ show, onHide, titl
useTranslation()
return (
<CommonModal show={show} onHide={onHide} titleI18nKey={titleI18nKey} icon={icon} closeButton={true}>
<CommonModal show={ show } onHide={ onHide } titleI18nKey={ titleI18nKey } icon={ icon } closeButton={ true }>
<Modal.Body className="text-dark">
{ children }
</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={onConfirm}>
<Trans i18nKey={deletionButtonI18nKey}/>
<Button variant="danger" onClick={ onConfirm }>
<Trans i18nKey={ deletionButtonI18nKey }/>
</Button>
</Modal.Footer>
</CommonModal>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Modal } from 'react-bootstrap'
@ -10,7 +10,7 @@ import { CommonModal, CommonModalProps } from './common-modal'
export const ErrorModal: React.FC<CommonModalProps> = ({ show, onHide, titleI18nKey, icon, children }) => {
return (
<CommonModal show={show} onHide={onHide} titleI18nKey={titleI18nKey} icon={icon} closeButton={true}>
<CommonModal show={ show } onHide={ onHide } titleI18nKey={ titleI18nKey } icon={ icon } closeButton={ true }>
<Modal.Body className="text-dark text-center">
{ children }
</Modal.Body>

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import equal from 'fast-deep-equal'
import React from 'react'
@ -23,16 +23,16 @@ export const MotdBanner: React.FC = () => {
}
return (
<ShowIf condition={bannerState.show}>
<ShowIf condition={ bannerState.show }>
<Alert variant='primary' dir='auto' className='mb-0 text-center d-flex flex-row justify-content-center'>
<Link to='/s/banner' className='flex-grow-1 align-self-center text-black'>
{bannerState.text}
{ bannerState.text }
</Link>
<Button
variant='outline-primary'
size='sm'
className='mx-2'
onClick={dismissBanner}>
onClick={ dismissBanner }>
<ForkAwesomeIcon icon='times'/>
</Button>
</Alert>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
export const createNumberRangeArray = (length: number) : number[] => {
return Array.from(Array(length).keys())
export const createNumberRangeArray = (length: number): number[] => {
return Array.from(Array(length)
.keys())
}

View file

@ -1,21 +1,21 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
export interface PageItemProps {
onClick: (index: number) => void
index: number
onClick: (index: number) => void
index: number
}
export const PagerItem: React.FC<PageItemProps> = ({ index, onClick }) => {
return (
<li className="page-item">
<span className="page-link" role="button" onClick={() => onClick(index)}>
{index + 1}
<span className="page-link" role="button" onClick={ () => onClick(index) }>
{ index + 1 }
</span>
</li>
)

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useEffect, useState } from 'react'
import { Pagination } from 'react-bootstrap'
@ -53,28 +53,31 @@ export const PagerPagination: React.FC<PaginationProps> = ({ numberOfPageButtons
0
)
const paginationItemsBefore = Array.from(new Array(correctedPageIndex - correctedLowerPageIndex)).map((k, index) => {
const itemIndex = correctedLowerPageIndex + index
return <PagerItem key={itemIndex} index={itemIndex} onClick={setPageIndex}/>
})
const paginationItemsBefore = Array.from(new Array(correctedPageIndex - correctedLowerPageIndex))
.map((k, index) => {
const itemIndex = correctedLowerPageIndex + index
return <PagerItem key={ itemIndex } index={ itemIndex }
onClick={ setPageIndex }/>
})
const paginationItemsAfter = Array.from(new Array(correctedUpperPageIndex - correctedPageIndex)).map((k, index) => {
const itemIndex = correctedPageIndex + index + 1
return <PagerItem key={itemIndex} index={itemIndex} onClick={setPageIndex}/>
})
const paginationItemsAfter = Array.from(new Array(correctedUpperPageIndex - correctedPageIndex))
.map((k, index) => {
const itemIndex = correctedPageIndex + index + 1
return <PagerItem key={ itemIndex } index={ itemIndex } onClick={ setPageIndex }/>
})
return (
<Pagination dir='ltr'>
<ShowIf condition={correctedLowerPageIndex > 0}>
<PagerItem key={0} index={0} onClick={setPageIndex}/>
<ShowIf condition={ correctedLowerPageIndex > 0 }>
<PagerItem key={ 0 } index={ 0 } onClick={ setPageIndex }/>
<Pagination.Ellipsis disabled/>
</ShowIf>
{paginationItemsBefore}
<Pagination.Item active>{correctedPageIndex + 1}</Pagination.Item>
{paginationItemsAfter}
<ShowIf condition={correctedUpperPageIndex < lastPageIndex}>
{ paginationItemsBefore }
<Pagination.Item active>{ correctedPageIndex + 1 }</Pagination.Item>
{ paginationItemsAfter }
<ShowIf condition={ correctedUpperPageIndex < lastPageIndex }>
<Pagination.Ellipsis disabled/>
<PagerItem key={lastPageIndex} index={lastPageIndex} onClick={setPageIndex}/>
<PagerItem key={ lastPageIndex } index={ lastPageIndex } onClick={ setPageIndex }/>
</ShowIf>
</Pagination>
)

View file

@ -1,15 +1,15 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useEffect } from 'react'
export interface PagerPageProps {
pageIndex: number
numberOfElementsPerPage: number
onLastPageIndexChange: (lastPageIndex: number) => void
pageIndex: number
numberOfElementsPerPage: number
onLastPageIndexChange: (lastPageIndex: number) => void
}
export const Pager: React.FC<PagerPageProps> = ({ children, numberOfElementsPerPage, pageIndex, onLastPageIndexChange }) => {
@ -22,10 +22,11 @@ export const Pager: React.FC<PagerPageProps> = ({ children, numberOfElementsPerP
return <Fragment>
{
React.Children.toArray(children).filter((value, index) => {
const pageOfElement = Math.floor((index) / numberOfElementsPerPage)
return (pageOfElement === correctedPageIndex)
})
React.Children.toArray(children)
.filter((value, index) => {
const pageOfElement = Math.floor((index) / numberOfElementsPerPage)
return (pageOfElement === correctedPageIndex)
})
}
</Fragment>
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { LandingLayout } from '../../landing-layout/landing-layout'

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useEffect, useState } from 'react'
import { Redirect } from 'react-router'
@ -27,7 +27,7 @@ export const Redirector: React.FC = () => {
if (error) {
return (<NotFoundErrorScreen/>)
} else if (!error && error != null) {
return (<Redirect to={`/n/${id}`}/>)
return (<Redirect to={ `/n/${ id }` }/>)
} else {
return (<span>Loading</span>)
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment } from 'react'
@ -11,5 +11,5 @@ export interface ShowIfProps {
}
export const ShowIf: React.FC<ShowIfProps> = ({ children, condition }) => {
return condition ? <Fragment>{children}</Fragment> : null
return condition ? <Fragment>{ children }</Fragment> : null
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { useTranslation } from 'react-i18next'
@ -21,15 +21,15 @@ const UserAvatar: React.FC<UserAvatarProps> = ({ name, photo, size, additionalCl
const { t } = useTranslation()
return (
<span className={'d-inline-flex align-items-center ' + additionalClasses}>
<span className={ 'd-inline-flex align-items-center ' + additionalClasses }>
<img
src={photo}
className={`user-avatar rounded mr-1 ${size ?? ''}`}
alt={t('common.avatarOf', { name })}
title={name}
src={ photo }
className={ `user-avatar rounded mr-1 ${ size ?? '' }` }
alt={ t('common.avatarOf', { name }) }
title={ name }
/>
<ShowIf condition={showName}>
<span className="mx-1 user-line-name">{name}</span>
<ShowIf condition={ showName }>
<span className="mx-1 user-line-name">{ name }</span>
</ShowIf>
</span>
)

View file

@ -17,11 +17,11 @@ export const ErrorWhileLoadingNoteAlert: React.FC<ErrorWhileLoadingNoteAlertProp
useTranslation()
return (
<ShowIf condition={show}>
<Alert variant={'danger'} className={'my-2'}>
<b><Trans i18nKey={'views.readOnly.error.title'}/></b>
<ShowIf condition={ show }>
<Alert variant={ 'danger' } className={ 'my-2' }>
<b><Trans i18nKey={ 'views.readOnly.error.title' }/></b>
<br/>
<Trans i18nKey={'views.readOnly.error.description'}/>
<Trans i18nKey={ 'views.readOnly.error.description' }/>
</Alert>
</ShowIf>
)

View file

@ -15,9 +15,9 @@ export interface LoadingNoteAlertProps {
export const LoadingNoteAlert: React.FC<LoadingNoteAlertProps> = ({ show }) => {
return (
<ShowIf condition={show}>
<Alert variant={'info'} className={'my-2'}>
<Trans i18nKey={'views.readOnly.loading'}/>
<ShowIf condition={ show }>
<Alert variant={ 'info' } className={ 'my-2' }>
<Trans i18nKey={ 'views.readOnly.loading' }/>
</Alert>
</ShowIf>
)

View file

@ -37,30 +37,32 @@ export const DocumentInfobar: React.FC<DocumentInfobarProps> = ({
const { t } = useTranslation()
return (
<div className={'d-flex flex-row my-3 document-infobar'}>
<div className={'col-md'}>&nbsp;</div>
<div className={'d-flex flex-fill'}>
<div className={'d-flex flex-column'}>
<div className={ 'd-flex flex-row my-3 document-infobar' }>
<div className={ 'col-md' }>&nbsp;</div>
<div className={ 'd-flex flex-fill' }>
<div className={ 'd-flex flex-column' }>
<DocumentInfoTimeLine
mode={DocumentInfoLineWithTimeMode.CREATED}
time={createdTime}
userName={createdAuthor}
profileImageSrc={'/img/avatar.png'}/>
mode={ DocumentInfoLineWithTimeMode.CREATED }
time={ createdTime }
userName={ createdAuthor }
profileImageSrc={ '/img/avatar.png' }/>
<DocumentInfoTimeLine
mode={DocumentInfoLineWithTimeMode.EDITED}
time={changedTime}
userName={changedAuthor}
profileImageSrc={'/img/avatar.png'}/>
mode={ DocumentInfoLineWithTimeMode.EDITED }
time={ changedTime }
userName={ changedAuthor }
profileImageSrc={ '/img/avatar.png' }/>
<hr/>
</div>
<span className={'ml-auto'}>
{ viewCount } <Trans i18nKey={'views.readOnly.viewCount'}/>
<ShowIf condition={editable}>
<InternalLink text={''} href={`/n/${noteId}`} icon={'pencil'} className={'text-primary text-decoration-none mx-1'} title={t('views.readOnly.editNote')}/>
<span className={ 'ml-auto' }>
{ viewCount } <Trans i18nKey={ 'views.readOnly.viewCount' }/>
<ShowIf condition={ editable }>
<InternalLink text={ '' } href={ `/n/${ noteId }` } icon={ 'pencil' }
className={ 'text-primary text-decoration-none mx-1' }
title={ t('views.readOnly.editNote') }/>
</ShowIf>
</span>
</div>
<div className={'col-md'}>&nbsp;</div>
<div className={ 'col-md' }>&nbsp;</div>
</div>
)
}

View file

@ -38,27 +38,27 @@ export const DocumentReadOnlyPage: React.FC = () => {
const noteDetails = useSelector((state: ApplicationState) => state.noteDetails)
return (
<div className={'d-flex flex-column mvh-100 bg-light'}>
<div className={ 'd-flex flex-column mvh-100 bg-light' }>
<MotdBanner/>
<AppBar mode={AppBarMode.BASIC}/>
<div className={'container'}>
<ErrorWhileLoadingNoteAlert show={error}/>
<LoadingNoteAlert show={loading}/>
<AppBar mode={ AppBarMode.BASIC }/>
<div className={ 'container' }>
<ErrorWhileLoadingNoteAlert show={ error }/>
<LoadingNoteAlert show={ loading }/>
</div>
<ShowIf condition={!error && !loading}>
<ShowIf condition={ !error && !loading }>
<DocumentInfobar
changedAuthor={noteDetails.lastChange.userId ?? ''}
changedTime={noteDetails.lastChange.timestamp}
createdAuthor={'Test'}
createdTime={noteDetails.createTime}
editable={true}
noteId={id}
viewCount={noteDetails.viewCount}
changedAuthor={ noteDetails.lastChange.userId ?? '' }
changedTime={ noteDetails.lastChange.timestamp }
createdAuthor={ 'Test' }
createdTime={ noteDetails.createTime }
editable={ true }
noteId={ id }
viewCount={ noteDetails.viewCount }
/>
<RenderIframe extraClasses={"flex-fill"}
markdownContent={markdownContent}
onFirstHeadingChange={onFirstHeadingChange}
onFrontmatterChange={onFrontmatterChange}/>
<RenderIframe extraClasses={ 'flex-fill' }
markdownContent={ markdownContent }
onFirstHeadingChange={ onFirstHeadingChange }
onFrontmatterChange={ onFrontmatterChange }/>
</ShowIf>
</div>
)

View file

@ -37,20 +37,21 @@ export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
const userExists = useSelector((state: ApplicationState) => !!state.user)
return (
<Navbar bg={'light'}>
<Navbar bg={ 'light' }>
<Nav className="mr-auto d-flex align-items-center">
<NavbarBranding/>
<ShowIf condition={mode === AppBarMode.EDITOR}>
<ShowIf condition={ mode === AppBarMode.EDITOR }>
<EditorViewMode/>
<SyncScrollButtons/>
</ShowIf>
<DarkModeButton/>
<Link to={`/p/${id}`} target='_blank'>
<Button title={t('editor.documentBar.slideMode')} className="ml-2 text-secondary" size="sm" variant="outline-light">
<Link to={ `/p/${ id }` } target='_blank'>
<Button title={ t('editor.documentBar.slideMode') } className="ml-2 text-secondary" size="sm"
variant="outline-light">
<ForkAwesomeIcon icon="television"/>
</Button>
</Link>
<ShowIf condition={mode === AppBarMode.EDITOR}>
<ShowIf condition={ mode === AppBarMode.EDITOR }>
<HelpButton/>
</ShowIf>
</Nav>
@ -58,11 +59,11 @@ export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
<Button className="mx-2" size="sm" variant="primary">
<ForkAwesomeIcon icon="plus"/> <Trans i18nKey="editor.appBar.new"/>
</Button>
<ShowIf condition={!userExists}>
<SignInButton size={'sm'} />
<ShowIf condition={ !userExists }>
<SignInButton size={ 'sm' }/>
</ShowIf>
<ShowIf condition={userExists}>
<UserDropdown />
<ShowIf condition={ userExists }>
<UserDropdown/>
</ShowIf>
</Nav>
</Navbar>

View file

@ -24,22 +24,22 @@ const DarkModeButton: React.FC = () => {
<ToggleButtonGroup
type="radio"
name="dark-mode"
value={darkModeEnabled}
value={ darkModeEnabled }
className="ml-2"
>
<ToggleButton
value={DarkModeState.DARK}
value={ DarkModeState.DARK }
variant="outline-secondary"
title={t('editor.darkMode.switchToDark')}
onChange={() => setDarkMode(true)}
title={ t('editor.darkMode.switchToDark') }
onChange={ () => setDarkMode(true) }
>
<ForkAwesomeIcon icon="moon"/>
</ToggleButton>
<ToggleButton
value={DarkModeState.LIGHT}
value={ DarkModeState.LIGHT }
variant="outline-secondary"
title={t('editor.darkMode.switchToLight')}
onChange={() => setDarkMode(false)}
title={ t('editor.darkMode.switchToLight') }
onChange={ () => setDarkMode(false) }
>
<ForkAwesomeIcon icon="sun-o"/>
</ToggleButton>

View file

@ -25,17 +25,17 @@ export const EditorViewMode: React.FC = () => {
<ToggleButtonGroup
type="radio"
name="options"
value={editorMode}
onChange={(value: EditorMode) => {
value={ editorMode }
onChange={ (value: EditorMode) => {
setEditorMode(value)
}}>
<ToggleButton value={EditorMode.PREVIEW} variant="outline-secondary" title={t('editor.viewMode.view')}>
} }>
<ToggleButton value={ EditorMode.PREVIEW } variant="outline-secondary" title={ t('editor.viewMode.view') }>
<ForkAwesomeIcon icon="eye"/>
</ToggleButton>
<ToggleButton value={EditorMode.BOTH} variant="outline-secondary" title={t('editor.viewMode.both')}>
<ToggleButton value={ EditorMode.BOTH } variant="outline-secondary" title={ t('editor.viewMode.both') }>
<ForkAwesomeIcon icon="columns"/>
</ToggleButton>
<ToggleButton value={EditorMode.EDITOR} variant="outline-secondary" title={t('editor.viewMode.edit')}>
<ToggleButton value={ EditorMode.EDITOR } variant="outline-secondary" title={ t('editor.viewMode.edit') }>
<ForkAwesomeIcon icon="pencil"/>
</ToggleButton>
</ToggleButtonGroup>

View file

@ -15,24 +15,24 @@ import './cheatsheet.scss'
export const Cheatsheet: React.FC = () => {
const { t } = useTranslation()
const codes = [
`**${t('editor.editorToolbar.bold')}**`,
`*${t('editor.editorToolbar.italic')}*`,
`++${t('editor.editorToolbar.underline')}++`,
`~~${t('editor.editorToolbar.strikethrough')}~~`,
`**${ t('editor.editorToolbar.bold') }**`,
`*${ t('editor.editorToolbar.italic') }*`,
`++${ t('editor.editorToolbar.underline') }++`,
`~~${ t('editor.editorToolbar.strikethrough') }~~`,
'H~2~O',
'19^th^',
`==${t('editor.help.cheatsheet.highlightedText')}==`,
`# ${t('editor.editorToolbar.header')}`,
`\`${t('editor.editorToolbar.code')}\``,
`==${ t('editor.help.cheatsheet.highlightedText') }==`,
`# ${ t('editor.editorToolbar.header') }`,
`\`${ t('editor.editorToolbar.code') }\``,
'```javascript=\nvar x = 5;\n```',
`> ${t('editor.editorToolbar.blockquote')}`,
`- ${t('editor.editorToolbar.unorderedList')}`,
`1. ${t('editor.editorToolbar.orderedList')}`,
`- [ ] ${t('editor.editorToolbar.checkList')}`,
`[${t('editor.editorToolbar.link')}](https://example.com)`,
`![${t('editor.editorToolbar.image')}](/icons/mstile-70x70.png)`,
`> ${ t('editor.editorToolbar.blockquote') }`,
`- ${ t('editor.editorToolbar.unorderedList') }`,
`1. ${ t('editor.editorToolbar.orderedList') }`,
`- [ ] ${ t('editor.editorToolbar.checkList') }`,
`[${ t('editor.editorToolbar.link') }](https://example.com)`,
`![${ t('editor.editorToolbar.image') }](/icons/mstile-70x70.png)`,
':smile:',
`:::info\n${t('editor.help.cheatsheet.exampleAlert')}\n:::`
`:::info\n${ t('editor.help.cheatsheet.exampleAlert') }\n:::`
]
const markdownIt = useMemo(() => {
@ -43,26 +43,26 @@ export const Cheatsheet: React.FC = () => {
return (
<Table className="table-condensed table-cheatsheet">
<thead>
<tr>
<th><Trans i18nKey='editor.help.cheatsheet.example'/></th>
<th><Trans i18nKey='editor.help.cheatsheet.syntax'/></th>
</tr>
<tr>
<th><Trans i18nKey='editor.help.cheatsheet.example'/></th>
<th><Trans i18nKey='editor.help.cheatsheet.syntax'/></th>
</tr>
</thead>
<tbody>
{codes.map((code, key) => {
return (
<tr key={key}>
<td>
<BasicMarkdownRenderer
content={code}
markdownIt={markdownIt}/>
</td>
<td className={'markdown-body'}>
<HighlightedCode code={code} wrapLines={true} startLineNumber={1} language={'markdown'}/>
</td>
</tr>
)
})}
{ codes.map((code, key) => {
return (
<tr key={ key }>
<td>
<BasicMarkdownRenderer
content={ code }
markdownIt={ markdownIt }/>
</td>
<td className={ 'markdown-body' }>
<HighlightedCode code={ code } wrapLines={ true } startLineNumber={ 1 } language={ 'markdown' }/>
</td>
</tr>
)
}) }
</tbody>
</Table>
)

View file

@ -13,9 +13,9 @@ import { Links } from './links'
import { Shortcut } from './shortcuts'
export enum HelpTabStatus {
Cheatsheet='cheatsheet.title',
Shortcuts='shortcuts.title',
Links='links.title'
Cheatsheet = 'cheatsheet.title',
Shortcuts = 'shortcuts.title',
Links = 'links.title'
}
export const HelpButton: React.FC = () => {
@ -36,35 +36,39 @@ export const HelpButton: React.FC = () => {
return (
<Fragment>
<Button title={t('editor.documentBar.help')} className='ml-2 text-secondary' size='sm' variant='outline-light'
onClick={() => setShow(true)}>
<Button title={ t('editor.documentBar.help') } className='ml-2 text-secondary' size='sm' variant='outline-light'
onClick={ () => setShow(true) }>
<ForkAwesomeIcon icon="question-circle"/>
</Button>
<Modal show={show} onHide={() => setShow(false)} animation={true} className='text-dark' size='lg'>
<Modal show={ show } onHide={ () => setShow(false) } animation={ true } className='text-dark' size='lg'>
<Modal.Header closeButton>
<Modal.Title>
<ForkAwesomeIcon icon='question-circle'/> <Trans i18nKey={'editor.documentBar.help'}/> <Trans i18nKey={`editor.help.${tab}`}/>
<ForkAwesomeIcon icon='question-circle'/> <Trans i18nKey={ 'editor.documentBar.help' }/> <Trans
i18nKey={ `editor.help.${ tab }` }/>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<nav className='nav nav-tabs'>
<Button variant={'light'} className={`nav-link nav-item ${tab === HelpTabStatus.Cheatsheet ? 'active' : ''}`}
onClick={() => setTab(HelpTabStatus.Cheatsheet)}
<Button variant={ 'light' }
className={ `nav-link nav-item ${ tab === HelpTabStatus.Cheatsheet ? 'active' : '' }` }
onClick={ () => setTab(HelpTabStatus.Cheatsheet) }
>
<Trans i18nKey={'editor.help.cheatsheet.title'}/>
<Trans i18nKey={ 'editor.help.cheatsheet.title' }/>
</Button>
<Button variant={'light'} className={`nav-link nav-item ${tab === HelpTabStatus.Shortcuts ? 'active' : ''}`}
onClick={() => setTab(HelpTabStatus.Shortcuts)}
<Button variant={ 'light' }
className={ `nav-link nav-item ${ tab === HelpTabStatus.Shortcuts ? 'active' : '' }` }
onClick={ () => setTab(HelpTabStatus.Shortcuts) }
>
<Trans i18nKey={'editor.help.shortcuts.title'}/>
<Trans i18nKey={ 'editor.help.shortcuts.title' }/>
</Button>
<Button variant={'light'} className={`nav-link nav-item ${tab === HelpTabStatus.Links ? 'active' : ''}`}
onClick={() => setTab(HelpTabStatus.Links)}
<Button variant={ 'light' }
className={ `nav-link nav-item ${ tab === HelpTabStatus.Links ? 'active' : '' }` }
onClick={ () => setTab(HelpTabStatus.Links) }
>
<Trans i18nKey={'editor.help.links.title'}/>
<Trans i18nKey={ 'editor.help.links.title' }/>
</Button>
</nav>
{tabContent()}
{ tabContent() }
</Modal.Body>
</Modal>
</Fragment>

View file

@ -18,15 +18,15 @@ export const Links: React.FC = () => {
const backendIssueTracker = useSelector((state: ApplicationState) => state.config.version.issueTrackerUrl)
return (
<Row className={'justify-content-center pt-4'}>
<Col lg={4}>
<Row className={ 'justify-content-center pt-4' }>
<Col lg={ 4 }>
<h3><Trans i18nKey='editor.help.contacts.title'/></h3>
<div>
<ul className="list-unstyled">
<li>
<TranslatedExternalLink
i18nKey='editor.help.contacts.community'
href={links.community}
href={ links.community }
icon='users'
className='text-primary'
/>
@ -34,8 +34,8 @@ export const Links: React.FC = () => {
<li>
<TranslatedExternalLink
i18nKey='editor.help.contacts.meetUsOn'
i18nOption={{ service: 'Matrix' }}
href={links.chat}
i18nOption={ { service: 'Matrix' } }
href={ links.chat }
icon='hashtag'
className='text-primary'
/>
@ -43,7 +43,7 @@ export const Links: React.FC = () => {
<li>
<TranslatedExternalLink
i18nKey='editor.help.contacts.reportIssue'
href={backendIssueTracker}
href={ backendIssueTracker }
icon='tag'
className='text-primary'
/>
@ -51,7 +51,7 @@ export const Links: React.FC = () => {
<li>
<TranslatedExternalLink
i18nKey='editor.help.contacts.helpTranslating'
href={links.translate}
href={ links.translate }
icon='language'
className='text-primary'
/>
@ -59,7 +59,7 @@ export const Links: React.FC = () => {
</ul>
</div>
</Col>
<Col lg={4}>
<Col lg={ 4 }>
<h3><Trans i18nKey='editor.help.documents.title'/></h3>
<div>
<ul className="list-unstyled">

View file

@ -13,7 +13,7 @@ export const Shortcut: React.FC = () => {
const modifierKey = isMac ? <kbd></kbd> : <kbd>Ctrl</kbd>
const altKey = isMac ? <kbd></kbd> : <kbd>Alt</kbd>
const shortcutMap: {[category: string]: { [functionName: string]: JSX.Element[] }} = {
const shortcutMap: { [category: string]: { [functionName: string]: JSX.Element[] } } = {
'View Mode': {
'editor.help.shortcuts.view': [<kbd>Ctrl</kbd>, <> + </>, altKey, <> + </>, <kbd>V</kbd>],
'editor.help.shortcuts.both': [<kbd>Ctrl</kbd>, <> + </>, altKey, <> + </>, <kbd>B</kbd>],
@ -29,28 +29,30 @@ export const Shortcut: React.FC = () => {
}
}
return (
<Row className={'justify-content-center pt-4'}>
{Object.keys(shortcutMap).map(category => {
return (
<Card key={category} className={'m-2 w-50'}>
<Card.Header>{category}</Card.Header>
<ListGroup variant="flush">
{Object.entries(shortcutMap[category]).map(([functionName, shortcuts]) => {
<Row className={ 'justify-content-center pt-4' }>
{ Object.keys(shortcutMap)
.map(category => {
return (
<ListGroup.Item key={functionName} className={'d-flex justify-content-between'}>
<span><Trans i18nKey={functionName}/></span>
<span>
<Card key={ category } className={ 'm-2 w-50' }>
<Card.Header>{ category }</Card.Header>
<ListGroup variant="flush">
{ Object.entries(shortcutMap[category])
.map(([functionName, shortcuts]) => {
return (
<ListGroup.Item key={ functionName } className={ 'd-flex justify-content-between' }>
<span><Trans i18nKey={ functionName }/></span>
<span>
{
shortcuts.map((shortcut, shortcutIndex) =>
<Fragment key={shortcutIndex}>{shortcut}</Fragment>)
<Fragment key={ shortcutIndex }>{ shortcut }</Fragment>)
}
</span>
</ListGroup.Item>
)
})}
</ListGroup>
</Card>)
})
</ListGroup.Item>
)
}) }
</ListGroup>
</Card>)
})
}
</Row>
)

View file

@ -22,9 +22,9 @@ export const NavbarBranding: React.FC = () => {
<Navbar.Brand>
<Link to="/intro" className="text-secondary text-decoration-none d-flex align-items-center">
<HedgeDocLogoWithText
logoType={darkModeActivated ? HedgeDocLogoType.WB_HORIZONTAL : HedgeDocLogoType.BW_HORIZONTAL}
size={HedgeDocLogoSize.SMALL}/>
<Branding inline={true}/>
logoType={ darkModeActivated ? HedgeDocLogoType.WB_HORIZONTAL : HedgeDocLogoType.BW_HORIZONTAL }
size={ HedgeDocLogoSize.SMALL }/>
<Branding inline={ true }/>
</Link>
</Navbar.Brand>
)

View file

@ -3,9 +3,9 @@
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns="http://www.w3.org/2000/svg"
width="512"
height="512"
viewBox="0 0 135.46666 135.46666"
@ -13,75 +13,75 @@
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="buttonIcon.svg">
<defs
id="defs2" />
<sodipodi:namedview
fit-margin-bottom="0"
fit-margin-right="0"
fit-margin-left="0"
fit-margin-top="0"
id="base"
pagecolor="#545b62"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="151.94971"
inkscape:cy="220.06486"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="3434"
inkscape:window-height="1321"
inkscape:window-x="0"
inkscape:window-y="84"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="false" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(253.17277,890.86874)"
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path864"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
d="m -185.5862,-882.45881 c -1.05348,0.0354 -2.05943,0.44876 -2.83393,1.16582 l -17.37672,16.08016 c -1.77986,1.64837 -1.89553,4.45148 -0.25787,6.24199 1.63744,1.79079 4.43758,1.90901 6.21875,0.262 l 14.39653,-13.32218 14.39654,13.32218 c 1.78118,1.64702 4.5813,1.5288 6.21874,-0.262 1.63691,-1.79129 1.52077,-4.59444 -0.25993,-6.24199 l -17.37465,-16.08016 c -0.84861,-0.78564 -1.97318,-1.20418 -3.12746,-1.16582 z" />
<path
id="path851"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
d="m -202.99806,-788.72004 c -1.12724,0.0475 -2.23794,0.52467 -3.05666,1.42007 -1.63766,1.79051 -1.52199,4.59363 0.25787,6.24199 l 17.37672,16.08016 c 0.7745,0.71706 1.78045,1.13042 2.83393,1.16582 1.15428,0.0384 2.27885,-0.38018 3.12746,-1.16582 l 17.37465,-16.08016 c 1.7807,-1.64754 1.89684,-4.4507 0.25993,-6.24199 -1.63744,-1.79081 -4.43756,-1.90902 -6.21874,-0.262 l -14.39654,13.32218 -14.39653,-13.32218 c -0.89058,-0.8235 -2.03485,-1.20561 -3.16209,-1.15807 z" />
<circle
r="16"
cy="-823.13544"
cx="-185.43944"
id="path845"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round" />
<defs
id="defs2"/>
<sodipodi:namedview
fit-margin-bottom="0"
fit-margin-right="0"
fit-margin-left="0"
fit-margin-top="0"
id="base"
pagecolor="#545b62"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="151.94971"
inkscape:cy="220.06486"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="3434"
inkscape:window-height="1321"
inkscape:window-x="0"
inkscape:window-y="84"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="false"/>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline;opacity:1;stroke:none;stroke-opacity:1"
id="g855">
<path
id="path858"
d="m -128.54012,-883.40784 -121.89532,111.71083 8.0967,8.83405 121.89531,-111.71083 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" />
<path
id="path860"
d="m -128.61914,-886.18945 a 2.7827783,2.7827783 0 0 0 -1.80078,0.73047 l -121.89453,111.71093 a 2.7827783,2.7827783 0 0 0 -0.17188,3.93164 l 8.09571,8.83399 a 2.7827783,2.7827783 0 0 0 3.93164,0.16992 l 121.89648,-111.70898 a 2.7827783,2.7827783 0 0 0 0.16992,-3.93164 l -8.0957,-8.83399 a 2.7827783,2.7827783 0 0 0 -2.13086,-0.90234 z m -0.0937,6.71289 4.33789,4.73047 -117.79297,107.95117 -4.33594,-4.73047 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#545b62;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" />
transform="translate(253.17277,890.86874)"
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path864"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
d="m -185.5862,-882.45881 c -1.05348,0.0354 -2.05943,0.44876 -2.83393,1.16582 l -17.37672,16.08016 c -1.77986,1.64837 -1.89553,4.45148 -0.25787,6.24199 1.63744,1.79079 4.43758,1.90901 6.21875,0.262 l 14.39653,-13.32218 14.39654,13.32218 c 1.78118,1.64702 4.5813,1.5288 6.21874,-0.262 1.63691,-1.79129 1.52077,-4.59444 -0.25993,-6.24199 l -17.37465,-16.08016 c -0.84861,-0.78564 -1.97318,-1.20418 -3.12746,-1.16582 z"/>
<path
id="path851"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
d="m -202.99806,-788.72004 c -1.12724,0.0475 -2.23794,0.52467 -3.05666,1.42007 -1.63766,1.79051 -1.52199,4.59363 0.25787,6.24199 l 17.37672,16.08016 c 0.7745,0.71706 1.78045,1.13042 2.83393,1.16582 1.15428,0.0384 2.27885,-0.38018 3.12746,-1.16582 l 17.37465,-16.08016 c 1.7807,-1.64754 1.89684,-4.4507 0.25993,-6.24199 -1.63744,-1.79081 -4.43756,-1.90902 -6.21874,-0.262 l -14.39654,13.32218 -14.39653,-13.32218 c -0.89058,-0.8235 -2.03485,-1.20561 -3.16209,-1.15807 z"/>
<circle
r="16"
cy="-823.13544"
cx="-185.43944"
id="path845"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round"/>
<g
style="display:inline;opacity:1;stroke:none;stroke-opacity:1"
id="g855">
<path
id="path858"
d="m -128.54012,-883.40784 -121.89532,111.71083 8.0967,8.83405 121.89531,-111.71083 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"/>
<path
id="path860"
d="m -128.61914,-886.18945 a 2.7827783,2.7827783 0 0 0 -1.80078,0.73047 l -121.89453,111.71093 a 2.7827783,2.7827783 0 0 0 -0.17188,3.93164 l 8.09571,8.83399 a 2.7827783,2.7827783 0 0 0 3.93164,0.16992 l 121.89648,-111.70898 a 2.7827783,2.7827783 0 0 0 0.16992,-3.93164 l -8.0957,-8.83399 a 2.7827783,2.7827783 0 0 0 -2.13086,-0.90234 z m -0.0937,6.71289 4.33789,4.73047 -117.79297,107.95117 -4.33594,-4.73047 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#545b62;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View file

@ -9,6 +9,7 @@
width: 20px;
height: 20px;
}
.btn {
svg g {
@import "../../../../style/variables.light";

View file

@ -24,21 +24,21 @@ export const SyncScrollButtons: React.FC = () => {
const { t } = useTranslation()
return (
<ToggleButtonGroup type="radio" defaultValue={[]} name="sync-scroll" className={'ml-2 sync-scroll-buttons'}
value={syncScrollEnabled}>
<ToggleButtonGroup type="radio" defaultValue={ [] } name="sync-scroll" className={ 'ml-2 sync-scroll-buttons' }
value={ syncScrollEnabled }>
<ToggleButton
variant={'outline-secondary'}
title={t('editor.appBar.syncScroll.enable')}
onChange={() => setEditorSyncScroll(true)}
value={SyncScrollState.SYNCED}
variant={ 'outline-secondary' }
title={ t('editor.appBar.syncScroll.enable') }
onChange={ () => setEditorSyncScroll(true) }
value={ SyncScrollState.SYNCED }
>
<EnabledScrollIcon/>
</ToggleButton>
<ToggleButton
variant={'outline-secondary'}
title={t('editor.appBar.syncScroll.disable')}
onChange={() => setEditorSyncScroll(false)}
value={SyncScrollState.UNSYNCED}
variant={ 'outline-secondary' }
title={ t('editor.appBar.syncScroll.disable') }
onChange={ () => setEditorSyncScroll(false) }
value={ SyncScrollState.UNSYNCED }
>
<DisabledScrollIcon/>
</ToggleButton>

View file

@ -15,10 +15,10 @@ export interface DocumentInfoLineProps {
export const DocumentInfoLine: React.FC<DocumentInfoLineProps> = ({ icon, size, children }) => {
return (
<span className={'d-flex align-items-center'}>
<ForkAwesomeIcon icon={icon} size={size} fixedWidth={true} className={'mx-2'}/>
<i className={'d-flex align-items-center'}>
{children}
<span className={ 'd-flex align-items-center' }>
<ForkAwesomeIcon icon={ icon } size={ size } fixedWidth={ true } className={ 'mx-2' }/>
<i className={ 'd-flex align-items-center' }>
{ children }
</i>
</span>
)

View file

@ -18,47 +18,49 @@ export interface DocumentInfoModalProps {
onHide: () => void
}
export const DocumentInfoModal: React.FC<DocumentInfoModalProps> = ({show, onHide}) => {
return (
<CommonModal
show={show}
onHide={onHide}
closeButton={true}
titleI18nKey={'editor.modal.documentInfo.title'}>
<Modal.Body>
<ListGroup>
<ListGroup.Item>
<DocumentInfoTimeLine
size={'2x'}
mode={DocumentInfoLineWithTimeMode.CREATED}
time={DateTime.local().minus({ days: 11 })}
userName={'Tilman'}
profileImageSrc={'/img/avatar.png'}/>
</ListGroup.Item>
<ListGroup.Item>
<DocumentInfoTimeLine
size={'2x'}
mode={DocumentInfoLineWithTimeMode.EDITED}
time={DateTime.local().minus({ minutes: 3 })}
userName={'Philip'}
profileImageSrc={'/img/avatar.png'}/>
</ListGroup.Item>
<ListGroup.Item>
<DocumentInfoLine icon={'users'} size={'2x'}>
<Trans i18nKey='editor.modal.documentInfo.usersContributed'>
<UnitalicBoldText text={'42'}/>
</Trans>
</DocumentInfoLine>
</ListGroup.Item>
<ListGroup.Item>
<DocumentInfoLine icon={'history'} size={'2x'}>
<Trans i18nKey='editor.modal.documentInfo.revisions'>
<UnitalicBoldText text={'192'}/>
</Trans>
</DocumentInfoLine>
</ListGroup.Item>
</ListGroup>
</Modal.Body>
</CommonModal>
);
export const DocumentInfoModal: React.FC<DocumentInfoModalProps> = ({ show, onHide }) => {
return (
<CommonModal
show={ show }
onHide={ onHide }
closeButton={ true }
titleI18nKey={ 'editor.modal.documentInfo.title' }>
<Modal.Body>
<ListGroup>
<ListGroup.Item>
<DocumentInfoTimeLine
size={ '2x' }
mode={ DocumentInfoLineWithTimeMode.CREATED }
time={ DateTime.local()
.minus({ days: 11 }) }
userName={ 'Tilman' }
profileImageSrc={ '/img/avatar.png' }/>
</ListGroup.Item>
<ListGroup.Item>
<DocumentInfoTimeLine
size={ '2x' }
mode={ DocumentInfoLineWithTimeMode.EDITED }
time={ DateTime.local()
.minus({ minutes: 3 }) }
userName={ 'Philip' }
profileImageSrc={ '/img/avatar.png' }/>
</ListGroup.Item>
<ListGroup.Item>
<DocumentInfoLine icon={ 'users' } size={ '2x' }>
<Trans i18nKey='editor.modal.documentInfo.usersContributed'>
<UnitalicBoldText text={ '42' }/>
</Trans>
</DocumentInfoLine>
</ListGroup.Item>
<ListGroup.Item>
<DocumentInfoLine icon={ 'history' } size={ '2x' }>
<Trans i18nKey='editor.modal.documentInfo.revisions'>
<UnitalicBoldText text={ '192' }/>
</Trans>
</DocumentInfoLine>
</ListGroup.Item>
</ListGroup>
</Modal.Body>
</CommonModal>
)
}

View file

@ -32,10 +32,11 @@ export const DocumentInfoTimeLine: React.FC<DocumentInfoLineWithTimeProps> = ({
const icon: IconName = mode === DocumentInfoLineWithTimeMode.CREATED ? 'plus' : 'pencil'
return (
<DocumentInfoLine icon={icon} size={size}>
<Trans i18nKey={i18nKey} >
<UserAvatar photo={profileImageSrc} additionalClasses={'font-style-normal bold font-weight-bold'} name={userName} size={size ? 'lg' : undefined}/>
<TimeFromNow time={time}/>
<DocumentInfoLine icon={ icon } size={ size }>
<Trans i18nKey={ i18nKey }>
<UserAvatar photo={ profileImageSrc } additionalClasses={ 'font-style-normal bold font-weight-bold' }
name={ userName } size={ size ? 'lg' : undefined }/>
<TimeFromNow time={ time }/>
</Trans>
</DocumentInfoLine>
)

View file

@ -14,6 +14,7 @@ export interface TimeFromNowProps {
export const TimeFromNow: React.FC<TimeFromNowProps> = ({ time }) => {
return (
<time className={'mx-1'} title={time.toFormat('DDDD T')} dateTime={time.toString()}>{time.toRelative()}</time>
<time className={ 'mx-1' } title={ time.toFormat('DDDD T') }
dateTime={ time.toString() }>{ time.toRelative() }</time>
)
}

View file

@ -7,9 +7,9 @@
import React from 'react'
export interface UnitalicBoldTextProps {
text: string ;
text: string;
}
export const UnitalicBoldText: React.FC<UnitalicBoldTextProps> = ({ text }) => {
return <b className={'font-style-normal mr-1'}>{text}</b>
return <b className={ 'font-style-normal mr-1' }>{ text }</b>
}

View file

@ -25,35 +25,35 @@ export const PermissionGroupEntry: React.FC<PermissionGroupEntryProps> = ({ titl
const { t } = useTranslation()
return (
<li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
<Trans i18nKey={title}/>
<li className={ 'list-group-item d-flex flex-row justify-content-between align-items-center' }>
<Trans i18nKey={ title }/>
<ToggleButtonGroup
type='radio'
name='edit-mode'
value={editMode}
onChange={onChangeEditMode}
value={ editMode }
onChange={ onChangeEditMode }
>
<ToggleButton
title={ t('editor.modal.permissions.denyGroup', { name: t(title) })}
variant={'light'}
className={'text-secondary'}
value={GroupMode.NONE}
title={ t('editor.modal.permissions.denyGroup', { name: t(title) }) }
variant={ 'light' }
className={ 'text-secondary' }
value={ GroupMode.NONE }
>
<ForkAwesomeIcon icon='ban'/>
</ToggleButton>
<ToggleButton
title={ t('editor.modal.permissions.viewOnlyGroup', { name: t(title) })}
variant={'light'}
className={'text-secondary'}
value={GroupMode.VIEW}
title={ t('editor.modal.permissions.viewOnlyGroup', { name: t(title) }) }
variant={ 'light' }
className={ 'text-secondary' }
value={ GroupMode.VIEW }
>
<ForkAwesomeIcon icon='eye'/>
</ToggleButton>
<ToggleButton
title={t('editor.modal.permissions.editGroup', { name: t(title) })}
variant={'light'}
className={'text-secondary'}
value={GroupMode.EDIT}
title={ t('editor.modal.permissions.editGroup', { name: t(title) }) }
variant={ 'light' }
className={ 'text-secondary' }
value={ GroupMode.EDIT }
>
<ForkAwesomeIcon icon='pencil'/>
</ToggleButton>

View file

@ -37,64 +37,64 @@ export const PermissionList: React.FC<PermissionListProps> = ({ list, identifier
}
return (
<ul className={'list-group'}>
{list.map(entry => (
<li key={entry.id} className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
{identifier(entry)}
<ul className={ 'list-group' }>
{ list.map(entry => (
<li key={ entry.id } className={ 'list-group-item d-flex flex-row justify-content-between align-items-center' }>
{ identifier(entry) }
<div>
<Button
variant='light'
className={'text-danger mr-2'}
title={t(removeI18nKey, { name: entry.name })}
onClick={() => removeEntry(entry.id)}
className={ 'text-danger mr-2' }
title={ t(removeI18nKey, { name: entry.name }) }
onClick={ () => removeEntry(entry.id) }
>
<ForkAwesomeIcon icon={'times'}/>
<ForkAwesomeIcon icon={ 'times' }/>
</Button>
<ToggleButtonGroup
type='radio'
name='edit-mode'
value={entry.canEdit ? EditMode.EDIT : EditMode.VIEW}
onChange={(value: EditMode) => changeEditMode(entry.id, value === EditMode.EDIT)}
value={ entry.canEdit ? EditMode.EDIT : EditMode.VIEW }
onChange={ (value: EditMode) => changeEditMode(entry.id, value === EditMode.EDIT) }
>
<ToggleButton
title={ t(viewI18nKey, { name: entry.name })}
variant={'light'}
className={'text-secondary'}
value={EditMode.VIEW}
title={ t(viewI18nKey, { name: entry.name }) }
variant={ 'light' }
className={ 'text-secondary' }
value={ EditMode.VIEW }
>
<ForkAwesomeIcon icon='eye'/>
</ToggleButton>
<ToggleButton
title={t(editI18nKey, { name: entry.name })}
variant={'light'}
className={'text-secondary'}
value={EditMode.EDIT}
title={ t(editI18nKey, { name: entry.name }) }
variant={ 'light' }
className={ 'text-secondary' }
value={ EditMode.EDIT }
>
<ForkAwesomeIcon icon='pencil'/>
</ToggleButton>
</ToggleButtonGroup>
</div>
</li>
))}
<li className={'list-group-item'}>
<form onSubmit={event => {
)) }
<li className={ 'list-group-item' }>
<form onSubmit={ event => {
event.preventDefault()
addEntry()
}}>
<InputGroup className={'mr-1 mb-1'}>
} }>
<InputGroup className={ 'mr-1 mb-1' }>
<FormControl
value={newEntry}
placeholder={t(addI18nKey)}
aria-label={t(addI18nKey)}
onChange={event => setNewEntry(event.currentTarget.value)}
value={ newEntry }
placeholder={ t(addI18nKey) }
aria-label={ t(addI18nKey) }
onChange={ event => setNewEntry(event.currentTarget.value) }
/>
<Button
variant='light'
className={'text-secondary ml-2'}
title={t(addI18nKey)}
onClick={addEntry}
className={ 'text-secondary ml-2' }
title={ t(addI18nKey) }
onClick={ addEntry }
>
<ForkAwesomeIcon icon={'plus'}/>
<ForkAwesomeIcon icon={ 'plus' }/>
</Button>
</InputGroup>
</form>

View file

@ -69,22 +69,26 @@ export const PermissionModal: React.FC<PermissionsModalProps> = ({ show, onHide
useEffect(() => {
// set owner
getUserById(permissionsApiResponse.owner).then(response => {
setOwner({
name: response.name,
photo: response.photo
getUserById(permissionsApiResponse.owner)
.then(response => {
setOwner({
name: response.name,
photo: response.photo
})
})
}).catch(() => setError(true))
.catch(() => setError(true))
// set user List
permissionsApiResponse.sharedTo.forEach(shareUser => {
getUserById(shareUser.username).then(response => {
setUserList(list => list.concat([{
id: response.id,
name: response.name,
photo: response.photo,
canEdit: shareUser.canEdit
}]))
}).catch(() => setError(true))
getUserById(shareUser.username)
.then(response => {
setUserList(list => list.concat([{
id: response.id,
name: response.name,
photo: response.photo,
canEdit: shareUser.canEdit
}]))
})
.catch(() => setError(true))
})
// set group List
permissionsApiResponse.sharedToGroup.forEach(sharedGroup => {
@ -122,45 +126,45 @@ export const PermissionModal: React.FC<PermissionsModalProps> = ({ show, onHide
return (
<CommonModal
show={show}
onHide={onHide}
closeButton={true}
titleI18nKey={'editor.modal.permissions.title'}>
show={ show }
onHide={ onHide }
closeButton={ true }
titleI18nKey={ 'editor.modal.permissions.title' }>
<Modal.Body>
<h5 className={'mb-3'}><Trans i18nKey={'editor.modal.permissions.owner'}/></h5>
<ShowIf condition={error}>
<h5 className={ 'mb-3' }><Trans i18nKey={ 'editor.modal.permissions.owner' }/></h5>
<ShowIf condition={ error }>
<Alert variant='danger'>
<Trans i18nKey='editor.modal.permissions.error'/>
</Alert>
</ShowIf>
<ul className={'list-group'}>
<li className={'list-group-item d-flex flex-row align-items-center'}>
<UserAvatar name={owner?.name ?? ''} photo={owner?.photo ?? ''}/>
<ul className={ 'list-group' }>
<li className={ 'list-group-item d-flex flex-row align-items-center' }>
<UserAvatar name={ owner?.name ?? '' } photo={ owner?.photo ?? '' }/>
</li>
</ul>
<h5 className={'my-3'}><Trans i18nKey={'editor.modal.permissions.sharedWithUsers'}/></h5>
<h5 className={ 'my-3' }><Trans i18nKey={ 'editor.modal.permissions.sharedWithUsers' }/></h5>
<PermissionList
list={userList}
identifier={entry => (<UserAvatar name={entry.name} photo={entry.photo}/>)}
changeEditMode={changeUserMode}
removeEntry={removeUser}
createEntry={addUser}
editI18nKey={'editor.modal.permissions.editUser'}
viewI18nKey={'editor.modal.permissions.viewOnlyUser'}
removeI18nKey={'editor.modal.permissions.removeUser'}
addI18nKey={'editor.modal.permissions.addUser'}
list={ userList }
identifier={ entry => (<UserAvatar name={ entry.name } photo={ entry.photo }/>) }
changeEditMode={ changeUserMode }
removeEntry={ removeUser }
createEntry={ addUser }
editI18nKey={ 'editor.modal.permissions.editUser' }
viewI18nKey={ 'editor.modal.permissions.viewOnlyUser' }
removeI18nKey={ 'editor.modal.permissions.removeUser' }
addI18nKey={ 'editor.modal.permissions.addUser' }
/>
<h5 className={'my-3'}><Trans i18nKey={'editor.modal.permissions.sharedWithGroups'}/></h5>
<ul className={'list-group'}>
<h5 className={ 'my-3' }><Trans i18nKey={ 'editor.modal.permissions.sharedWithGroups' }/></h5>
<ul className={ 'list-group' }>
<PermissionGroupEntry
title={'editor.modal.permissions.allUser'}
editMode={allUserPermissions}
onChangeEditMode={setAllUserPermissions}
title={ 'editor.modal.permissions.allUser' }
editMode={ allUserPermissions }
onChangeEditMode={ setAllUserPermissions }
/>
<PermissionGroupEntry
title={'editor.modal.permissions.allLoggedInUser'}
editMode={allLoggedInUserPermissions}
onChangeEditMode={setAllLoggedInUserPermissions}
title={ 'editor.modal.permissions.allLoggedInUser' }
editMode={ allLoggedInUserPermissions }
onChangeEditMode={ setAllLoggedInUserPermissions }
/>
</ul>
</Modal.Body>

View file

@ -23,26 +23,29 @@ export interface RevisionModalListEntryProps {
export const RevisionModalListEntry: React.FC<RevisionModalListEntryProps> = ({ active, onClick, revision, revisionAuthorListMap }) => (
<ListGroup.Item
as='li'
active={active}
onClick={onClick}
active={ active }
onClick={ onClick }
className='user-select-none revision-item d-flex flex-column'
>
<span>
<ForkAwesomeIcon icon={'clock-o'} className='mx-2'/>
{DateTime.fromMillis(revision.timestamp * 1000).toFormat('DDDD T')}
<ForkAwesomeIcon icon={ 'clock-o' } className='mx-2'/>
{ DateTime.fromMillis(revision.timestamp * 1000)
.toFormat('DDDD T') }
</span>
<span>
<ForkAwesomeIcon icon={'file-text-o'} className='mx-2'/>
<Trans i18nKey={'editor.modal.revision.length'}/>: {revision.length}
<ForkAwesomeIcon icon={ 'file-text-o' } className='mx-2'/>
<Trans i18nKey={ 'editor.modal.revision.length' }/>: { revision.length }
</span>
<span className={'d-flex flex-row my-1 align-items-center'}>
<ForkAwesomeIcon icon={'user-o'} className={'mx-2'}/>
<span className={ 'd-flex flex-row my-1 align-items-center' }>
<ForkAwesomeIcon icon={ 'user-o' } className={ 'mx-2' }/>
{
revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => {
return (
<UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/>
)
})
revisionAuthorListMap.get(revision.timestamp)
?.map((user, index) => {
return (
<UserAvatar name={ user.name } photo={ user.photo } showName={ false }
additionalClasses={ 'mx-1' } key={ index }/>
)
})
}
</span>
</ListGroup.Item>

View file

@ -36,61 +36,66 @@ export const RevisionModal: React.FC<PermissionsModalProps> = ({ show, onHide })
const { id } = useParams<{ id: string }>()
useEffect(() => {
getAllRevisions(id).then(fetchedRevisions => {
fetchedRevisions.forEach(revision => {
const authorData = getUserDataForRevision(revision.authors)
revisionAuthorListMap.current.set(revision.timestamp, authorData)
getAllRevisions(id)
.then(fetchedRevisions => {
fetchedRevisions.forEach(revision => {
const authorData = getUserDataForRevision(revision.authors)
revisionAuthorListMap.current.set(revision.timestamp, authorData)
})
setRevisions(fetchedRevisions)
if (fetchedRevisions.length >= 1) {
setSelectedRevisionTimestamp(fetchedRevisions[0].timestamp)
}
})
setRevisions(fetchedRevisions)
if (fetchedRevisions.length >= 1) {
setSelectedRevisionTimestamp(fetchedRevisions[0].timestamp)
}
}).catch(() => setError(true))
.catch(() => setError(true))
}, [setRevisions, setError, id])
useEffect(() => {
if (selectedRevisionTimestamp === null) {
return
}
getRevision(id, selectedRevisionTimestamp).then(fetchedRevision => {
setSelectedRevision(fetchedRevision)
}).catch(() => setError(true))
getRevision(id, selectedRevisionTimestamp)
.then(fetchedRevision => {
setSelectedRevision(fetchedRevision)
})
.catch(() => setError(true))
}, [selectedRevisionTimestamp, id])
const markdownContent = useNoteMarkdownContent()
return (
<CommonModal show={show} onHide={onHide} titleI18nKey={'editor.modal.revision.title'} icon={'history'} closeButton={true} size={'xl'} additionalClasses='revision-modal'>
<CommonModal show={ show } onHide={ onHide } titleI18nKey={ 'editor.modal.revision.title' } icon={ 'history' }
closeButton={ true } size={ 'xl' } additionalClasses='revision-modal'>
<Modal.Body>
<Row>
<Col lg={4} className={'scroll-col'}>
<Col lg={ 4 } className={ 'scroll-col' }>
<ListGroup as='ul'>
{
revisions.map((revision, revisionIndex) => (
<RevisionModalListEntry
key={revisionIndex}
active={selectedRevisionTimestamp === revision.timestamp}
revision={revision}
revisionAuthorListMap={revisionAuthorListMap.current}
onClick={() => setSelectedRevisionTimestamp(revision.timestamp)}
key={ revisionIndex }
active={ selectedRevisionTimestamp === revision.timestamp }
revision={ revision }
revisionAuthorListMap={ revisionAuthorListMap.current }
onClick={ () => setSelectedRevisionTimestamp(revision.timestamp) }
/>
))
}
</ListGroup>
</Col>
<Col lg={8} className={'scroll-col'}>
<ShowIf condition={error}>
<Col lg={ 8 } className={ 'scroll-col' }>
<ShowIf condition={ error }>
<Alert variant='danger'>
<Trans i18nKey='editor.modal.revision.error'/>
</Alert>
</ShowIf>
<ShowIf condition={!error && !!selectedRevision}>
<ShowIf condition={ !error && !!selectedRevision }>
<ReactDiffViewer
oldValue={selectedRevision?.content}
newValue={markdownContent}
splitView={false}
compareMethod={DiffMethod.WORDS}
useDarkTheme={darkModeEnabled}
oldValue={ selectedRevision?.content }
newValue={ markdownContent }
splitView={ false }
compareMethod={ DiffMethod.WORDS }
useDarkTheme={ darkModeEnabled }
/>
</ShowIf>
</Col>
@ -99,20 +104,20 @@ export const RevisionModal: React.FC<PermissionsModalProps> = ({ show, onHide })
<Modal.Footer>
<Button
variant='secondary'
onClick={onHide}>
<Trans i18nKey={'common.close'}/>
onClick={ onHide }>
<Trans i18nKey={ 'common.close' }/>
</Button>
<Button
variant='danger'
disabled={!selectedRevisionTimestamp}
onClick={() => window.alert('Not yet implemented. Requires websocket.')}>
<Trans i18nKey={'editor.modal.revision.revertButton'}/>
disabled={ !selectedRevisionTimestamp }
onClick={ () => window.alert('Not yet implemented. Requires websocket.') }>
<Trans i18nKey={ 'editor.modal.revision.revertButton' }/>
</Button>
<Button
variant='primary'
disabled={!selectedRevisionTimestamp}
onClick={() => downloadRevision(id, selectedRevision)}>
<Trans i18nKey={'editor.modal.revision.download'}/>
disabled={ !selectedRevisionTimestamp }
onClick={ () => downloadRevision(id, selectedRevision) }>
<Trans i18nKey={ 'editor.modal.revision.download' }/>
</Button>
</Modal.Footer>
</CommonModal>

View file

@ -13,7 +13,7 @@ export const downloadRevision = (noteId: string, revision: Revision | null): voi
if (!revision) {
return
}
download(revision.content, `${noteId}-${revision.timestamp}.md`, 'text/markdown')
download(revision.content, `${ noteId }-${ revision.timestamp }.md`, 'text/markdown')
}
export const getUserDataForRevision = (authors: string[]): UserResponse[] => {

View file

@ -31,21 +31,23 @@ export const ShareModal: React.FC<ShareModalProps> = ({ show, onHide }) => {
return (
<CommonModal
show={show}
onHide={onHide}
closeButton={true}
titleI18nKey={'editor.modal.shareLink.title'}>
show={ show }
onHide={ onHide }
closeButton={ true }
titleI18nKey={ 'editor.modal.shareLink.title' }>
<Modal.Body>
<Trans i18nKey={'editor.modal.shareLink.editorDescription'}/>
<CopyableField content={`${baseUrl}/n/${id}?${editorMode}`} nativeShareButton={true}
url={`${baseUrl}/n/${id}?${editorMode}`}/>
<ShowIf condition={noteFrontmatter.type === 'slide'}>
<Trans i18nKey={'editor.modal.shareLink.slidesDescription'}/>
<CopyableField content={`${baseUrl}/p/${id}`} nativeShareButton={true} url={`${baseUrl}/p/${id}`}/>
<Trans i18nKey={ 'editor.modal.shareLink.editorDescription' }/>
<CopyableField content={ `${ baseUrl }/n/${ id }?${ editorMode }` } nativeShareButton={ true }
url={ `${ baseUrl }/n/${ id }?${ editorMode }` }/>
<ShowIf condition={ noteFrontmatter.type === 'slide' }>
<Trans i18nKey={ 'editor.modal.shareLink.slidesDescription' }/>
<CopyableField content={ `${ baseUrl }/p/${ id }` } nativeShareButton={ true }
url={ `${ baseUrl }/p/${ id }` }/>
</ShowIf>
<ShowIf condition={noteFrontmatter.type === ''}>
<Trans i18nKey={'editor.modal.shareLink.viewOnlyDescription'}/>
<CopyableField content={`${baseUrl}/s/${id}`} nativeShareButton={true} url={`${baseUrl}/s/${id}`}/>
<ShowIf condition={ noteFrontmatter.type === '' }>
<Trans i18nKey={ 'editor.modal.shareLink.viewOnlyDescription' }/>
<CopyableField content={ `${ baseUrl }/s/${ id }` } nativeShareButton={ true }
url={ `${ baseUrl }/s/${ id }` }/>
</ShowIf>
</Modal.Body>
</CommonModal>

View file

@ -8,8 +8,8 @@ import { RefObject, useCallback } from 'react'
import { LineMarkerPosition } from '../../../markdown-renderer/types'
export const useAdaptedLineMarkerCallback = (documentRenderPaneRef: RefObject<HTMLDivElement> | undefined,
rendererRef: RefObject<HTMLDivElement>,
onLineMarkerPositionChanged: ((lineMarkerPosition: LineMarkerPosition[]) => void) | undefined): ((lineMarkerPosition: LineMarkerPosition[]) => void) => {
rendererRef: RefObject<HTMLDivElement>,
onLineMarkerPositionChanged: ((lineMarkerPosition: LineMarkerPosition[]) => void) | undefined): ((lineMarkerPosition: LineMarkerPosition[]) => void) => {
return useCallback((linkMarkerPositions) => {
if (!onLineMarkerPositionChanged || !documentRenderPaneRef || !documentRenderPaneRef.current || !rendererRef.current) {
return

View file

@ -8,7 +8,7 @@ import { RefObject, useCallback, useRef } from 'react'
import { IframeEditorToRendererCommunicator } from '../../../render-page/iframe-editor-to-renderer-communicator'
export const useOnIframeLoad = (frameReference: RefObject<HTMLIFrameElement>, iframeCommunicator: IframeEditorToRendererCommunicator,
rendererOrigin: string, renderPageUrl: string, onNavigateAway: () => void): () => void => {
rendererOrigin: string, renderPageUrl: string, onNavigateAway: () => void): () => void => {
const sendToRenderPage = useRef<boolean>(true)
return useCallback(() => {
@ -24,7 +24,7 @@ export const useOnIframeLoad = (frameReference: RefObject<HTMLIFrameElement>, if
return
} else {
onNavigateAway()
console.error("Navigated away from unknown URL")
console.error('Navigated away from unknown URL')
frame.src = renderPageUrl
sendToRenderPage.current = true
}

View file

@ -26,7 +26,7 @@ export const ShowOnPropChangeImageLightbox: React.FC<ShowOnPropChangeImageLightb
}, [details])
return (
<ImageLightboxModal show={show} onHide={hideLightbox} src={details?.src}
alt={details?.alt} title={details?.title}/>
<ImageLightboxModal show={ show } onHide={ hideLightbox } src={ details?.src }
alt={ details?.alt } title={ details?.title }/>
)
}

View file

@ -19,13 +19,14 @@ export const MaxLengthWarningModal: React.FC<MaxLengthWarningModalProps> = ({ sh
useTranslation()
return (
<CommonModal data-cy={'limitReachedModal'} show={show} onHide={onHide} titleI18nKey={'editor.error.limitReached.title'} closeButton={true}>
<CommonModal data-cy={ 'limitReachedModal' } show={ show } onHide={ onHide }
titleI18nKey={ 'editor.error.limitReached.title' } closeButton={ true }>
<Modal.Body>
<Trans i18nKey={'editor.error.limitReached.description'} values={{ maxLength }} />
<strong className='mt-2 d-block'><Trans i18nKey={'editor.error.limitReached.advice'}/></strong>
<Trans i18nKey={ 'editor.error.limitReached.description' } values={ { maxLength } }/>
<strong className='mt-2 d-block'><Trans i18nKey={ 'editor.error.limitReached.advice' }/></strong>
</Modal.Body>
<Modal.Footer>
<Button onClick={onHide}><Trans i18nKey={'common.close'}/></Button>
<Button onClick={ onHide }><Trans i18nKey={ 'common.close' }/></Button>
</Modal.Footer>
</CommonModal>
)

View file

@ -58,7 +58,8 @@ export const EditorPage: React.FC = () => {
useEffect(() => {
const requestedMode = search.substr(1)
const mode = Object.values(EditorMode).find(mode => mode === requestedMode)
const mode = Object.values(EditorMode)
.find(mode => mode === requestedMode)
if (mode) {
setEditorMode(mode)
}
@ -92,37 +93,37 @@ export const EditorPage: React.FC = () => {
return (
<Fragment>
<MotdBanner/>
<div className={'d-flex flex-column vh-100'}>
<AppBar mode={AppBarMode.EDITOR}/>
<div className={ 'd-flex flex-column vh-100' }>
<AppBar mode={ AppBarMode.EDITOR }/>
<div className={'container'}>
<ErrorWhileLoadingNoteAlert show={error}/>
<LoadingNoteAlert show={loading}/>
<div className={ 'container' }>
<ErrorWhileLoadingNoteAlert show={ error }/>
<LoadingNoteAlert show={ loading }/>
</div>
<ShowIf condition={!error && !loading}>
<div className={"flex-fill d-flex h-100 w-100 overflow-hidden flex-row"}>
<ShowIf condition={ !error && !loading }>
<div className={ 'flex-fill d-flex h-100 w-100 overflow-hidden flex-row' }>
<Splitter
showLeft={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}
showLeft={ editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH }
left={
<EditorPane
onContentChange={setNoteMarkdownContent}
content={markdownContent}
scrollState={scrollState.editorScrollState}
onScroll={onEditorScroll}
onMakeScrollSource={setEditorToScrollSource}/>
onContentChange={ setNoteMarkdownContent }
content={ markdownContent }
scrollState={ scrollState.editorScrollState }
onScroll={ onEditorScroll }
onMakeScrollSource={ setEditorToScrollSource }/>
}
showRight={editorMode === EditorMode.PREVIEW || editorMode === EditorMode.BOTH}
showRight={ editorMode === EditorMode.PREVIEW || editorMode === EditorMode.BOTH }
right={
<RenderIframe
markdownContent={markdownContent}
onMakeScrollSource={setRendererToScrollSource}
onFirstHeadingChange={updateNoteTitleByFirstHeading}
onTaskCheckedChange={SetCheckboxInMarkdownContent}
onFrontmatterChange={setNoteFrontmatter}
onScroll={onMarkdownRendererScroll}
scrollState={scrollState.rendererScrollState}/>
markdownContent={ markdownContent }
onMakeScrollSource={ setRendererToScrollSource }
onFirstHeadingChange={ updateNoteTitleByFirstHeading }
onTaskCheckedChange={ SetCheckboxInMarkdownContent }
onFrontmatterChange={ setNoteFrontmatter }
onScroll={ onMarkdownRendererScroll }
scrollState={ scrollState.rendererScrollState }/>
}
containerClassName={'overflow-hidden'}/>
containerClassName={ 'overflow-hidden' }/>
<Sidebar/>
</div>
</ShowIf>

View file

@ -10,7 +10,7 @@ import { findWordAtCursor, Hinter, search } from './index'
const wordRegExp = /^```((\w|-|_|\+)*)$/
let allSupportedLanguages: string[] = []
const codeBlockHint = (editor: Editor): Promise< Hints| null > => {
const codeBlockHint = (editor: Editor): Promise<Hints | null> => {
return import(/* webpackChunkName: "highlight.js" */ '../../../common/hljs/hljs').then((hljs) =>
new Promise((resolve) => {
const searchTerm = findWordAtCursor(editor)
@ -21,7 +21,8 @@ const codeBlockHint = (editor: Editor): Promise< Hints| null > => {
}
const term = searchResult[1]
if (allSupportedLanguages.length === 0) {
allSupportedLanguages = hljs.default.listLanguages().concat('csv', 'flow', 'html', 'js', 'markmap', 'abc', 'graphviz', 'mermaid', 'vega-lite')
allSupportedLanguages = hljs.default.listLanguages()
.concat('csv', 'flow', 'html', 'js', 'markmap', 'abc', 'graphviz', 'mermaid', 'vega-lite')
}
const suggestions = search(term, allSupportedLanguages)
const cursor = editor.getCursor()

View file

@ -9,7 +9,7 @@ import { findWordAtCursor, Hinter } from './index'
const wordRegExp = /^(<d(?:e|et|eta|etai|etail|etails)?)$/
const collapsableBlockHint = (editor: Editor): Promise< Hints| null > => {
const collapsableBlockHint = (editor: Editor): Promise<Hints | null> => {
return new Promise((resolve) => {
const searchTerm = findWordAtCursor(editor)
const searchResult = wordRegExp.exec(searchTerm.text)

View file

@ -16,7 +16,8 @@ const spoilerSuggestion: Hint = {
const suggestions = validAlertLevels.map((suggestion: string): Hint => ({
text: ':::' + suggestion + '\n\n::: \n',
displayText: suggestion
})).concat(spoilerSuggestion)
}))
.concat(spoilerSuggestion)
const containerHint = (editor: Editor): Promise<Hints | null> => {
return new Promise((resolve) => {

View file

@ -31,7 +31,7 @@ const findEmojiInDatabase = async (emojiIndex: Database, term: string): Promise<
}
}
const convertEmojiEventToHint = (emojiData:EmojiClickEventDetail): Hint | undefined => {
const convertEmojiEventToHint = (emojiData: EmojiClickEventDetail): Hint | undefined => {
const shortCode = getEmojiShortCode(emojiData)
if (!shortCode) {
return undefined
@ -40,7 +40,7 @@ const convertEmojiEventToHint = (emojiData:EmojiClickEventDetail): Hint | undefi
text: shortCode,
render: (parent: HTMLLIElement) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = `${getEmojiIcon(emojiData)} ${shortCode}`
wrapper.innerHTML = `${ getEmojiIcon(emojiData) } ${ shortCode }`
parent.appendChild(wrapper)
}
}

View file

@ -11,7 +11,7 @@ const wordRegExp = /^(\s{0,3})(#{1,6})$/
const allSupportedHeaders = ['# h1', '## h2', '### h3', '#### h4', '##### h5', '###### h6', '###### tags: `example`']
const allSupportedHeadersTextToInsert = ['# ', '## ', '### ', '#### ', '##### ', '###### ', '###### tags: `example`']
const headerHint = (editor: Editor): Promise< Hints| null > => {
const headerHint = (editor: Editor): Promise<Hints | null> => {
return new Promise((resolve) => {
const searchTerm = findWordAtCursor(editor)
const searchResult = wordRegExp.exec(searchTerm.text)

View file

@ -14,7 +14,7 @@ const allSupportedImages = [
'![image alt][reference]'
]
const imageHint = (editor: Editor): Promise< Hints| null > => {
const imageHint = (editor: Editor): Promise<Hints | null> => {
return new Promise((resolve) => {
const searchTerm = findWordAtCursor(editor)
const searchResult = wordRegExp.exec(searchTerm.text)

Some files were not shown because too many files have changed in this diff Show more