Merge pull request #3943 from atom/ns-text-editor-shadow-dom
Render text editor contents inside shadow DOM
Esse commit está contido em:
@@ -15,6 +15,9 @@
|
|||||||
'shift-tab': 'editor:outdent-selected-rows'
|
'shift-tab': 'editor:outdent-selected-rows'
|
||||||
'ctrl-K': 'editor:delete-line'
|
'ctrl-K': 'editor:delete-line'
|
||||||
|
|
||||||
|
'.select-list atom-text-editor.mini':
|
||||||
|
'enter': 'core:confirm'
|
||||||
|
|
||||||
'.tool-panel.panel-left, .tool-panel.panel-right':
|
'.tool-panel.panel-left, .tool-panel.panel-right':
|
||||||
'escape': 'tool-panel:unfocus'
|
'escape': 'tool-panel:unfocus'
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -73,7 +73,7 @@
|
|||||||
"solarized-dark-syntax": "0.22.0",
|
"solarized-dark-syntax": "0.22.0",
|
||||||
"solarized-light-syntax": "0.12.0",
|
"solarized-light-syntax": "0.12.0",
|
||||||
"archive-view": "0.37.0",
|
"archive-view": "0.37.0",
|
||||||
"autocomplete": "0.32.0",
|
"autocomplete": "0.33.0",
|
||||||
"autoflow": "0.18.0",
|
"autoflow": "0.18.0",
|
||||||
"autosave": "0.18.0",
|
"autosave": "0.18.0",
|
||||||
"background-tips": "0.17.0",
|
"background-tips": "0.17.0",
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"markdown-preview": "0.107.0",
|
"markdown-preview": "0.107.0",
|
||||||
"metrics": "0.36.0",
|
"metrics": "0.36.0",
|
||||||
"open-on-github": "0.30.0",
|
"open-on-github": "0.30.0",
|
||||||
"package-generator": "0.31.0",
|
"package-generator": "0.32.0",
|
||||||
"release-notes": "0.36.0",
|
"release-notes": "0.36.0",
|
||||||
"settings-view": "0.154.0",
|
"settings-view": "0.154.0",
|
||||||
"snippets": "0.56.0",
|
"snippets": "0.56.0",
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
a { color: red }
|
||||||
@@ -48,9 +48,10 @@ describe "PackageManager", ->
|
|||||||
describe "when called multiple times", ->
|
describe "when called multiple times", ->
|
||||||
it "it only calls activate on the package once", ->
|
it "it only calls activate on the package once", ->
|
||||||
spyOn(Package.prototype, 'activateNow').andCallThrough()
|
spyOn(Package.prototype, 'activateNow').andCallThrough()
|
||||||
atom.packages.activatePackage('package-with-index')
|
waitsForPromise ->
|
||||||
atom.packages.activatePackage('package-with-index')
|
atom.packages.activatePackage('package-with-index')
|
||||||
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage('package-with-index')
|
||||||
waitsForPromise ->
|
waitsForPromise ->
|
||||||
atom.packages.activatePackage('package-with-index')
|
atom.packages.activatePackage('package-with-index')
|
||||||
|
|
||||||
@@ -182,8 +183,10 @@ describe "PackageManager", ->
|
|||||||
pack.mainModule.someNumber = 77
|
pack.mainModule.someNumber = 77
|
||||||
atom.packages.deactivatePackage("package-with-serialization")
|
atom.packages.deactivatePackage("package-with-serialization")
|
||||||
spyOn(pack.mainModule, 'activate').andCallThrough()
|
spyOn(pack.mainModule, 'activate').andCallThrough()
|
||||||
atom.packages.activatePackage("package-with-serialization")
|
waitsForPromise ->
|
||||||
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
atom.packages.activatePackage("package-with-serialization")
|
||||||
|
runs ->
|
||||||
|
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
||||||
|
|
||||||
it "logs warning instead of throwing an exception if the package fails to load", ->
|
it "logs warning instead of throwing an exception if the package fails to load", ->
|
||||||
atom.config.set("core.disabledPackages", [])
|
atom.config.set("core.disabledPackages", [])
|
||||||
@@ -202,11 +205,13 @@ describe "PackageManager", ->
|
|||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])).toHaveLength 0
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])).toHaveLength 0
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
|
||||||
|
|
||||||
atom.packages.activatePackage("package-with-keymaps")
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage("package-with-keymaps")
|
||||||
|
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe "test-1"
|
runs ->
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])[0].command).toBe "test-2"
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe "test-1"
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])[0].command).toBe "test-2"
|
||||||
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
|
||||||
|
|
||||||
describe "when the metadata contains a 'keymaps' manifest", ->
|
describe "when the metadata contains a 'keymaps' manifest", ->
|
||||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||||
@@ -215,11 +220,13 @@ describe "PackageManager", ->
|
|||||||
|
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
|
||||||
|
|
||||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||||
|
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe 'keymap-1'
|
runs ->
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-n', target:element1[0])[0].command).toBe 'keymap-2'
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe 'keymap-1'
|
||||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-y', target:element3[0])).toHaveLength 0
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-n', target:element1[0])[0].command).toBe 'keymap-2'
|
||||||
|
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-y', target:element3[0])).toHaveLength 0
|
||||||
|
|
||||||
describe "menu loading", ->
|
describe "menu loading", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@@ -232,14 +239,16 @@ describe "PackageManager", ->
|
|||||||
|
|
||||||
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
||||||
|
|
||||||
atom.packages.activatePackage("package-with-menus")
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage("package-with-menus")
|
||||||
|
|
||||||
expect(atom.menu.template.length).toBe 2
|
runs ->
|
||||||
expect(atom.menu.template[0].label).toBe "Second to Last"
|
expect(atom.menu.template.length).toBe 2
|
||||||
expect(atom.menu.template[1].label).toBe "Last"
|
expect(atom.menu.template[0].label).toBe "Second to Last"
|
||||||
expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 1"
|
expect(atom.menu.template[1].label).toBe "Last"
|
||||||
expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 2"
|
expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 1"
|
||||||
expect(atom.contextMenu.templateForElement(element)[2].label).toBe "Menu item 3"
|
expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 2"
|
||||||
|
expect(atom.contextMenu.templateForElement(element)[2].label).toBe "Menu item 3"
|
||||||
|
|
||||||
describe "when the metadata contains a 'menus' manifest", ->
|
describe "when the metadata contains a 'menus' manifest", ->
|
||||||
it "loads only the menus specified by the manifest, in the specified order", ->
|
it "loads only the menus specified by the manifest, in the specified order", ->
|
||||||
@@ -247,13 +256,15 @@ describe "PackageManager", ->
|
|||||||
|
|
||||||
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
||||||
|
|
||||||
atom.packages.activatePackage("package-with-menus-manifest")
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage("package-with-menus-manifest")
|
||||||
|
|
||||||
expect(atom.menu.template[0].label).toBe "Second to Last"
|
runs ->
|
||||||
expect(atom.menu.template[1].label).toBe "Last"
|
expect(atom.menu.template[0].label).toBe "Second to Last"
|
||||||
expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 2"
|
expect(atom.menu.template[1].label).toBe "Last"
|
||||||
expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 1"
|
expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 2"
|
||||||
expect(atom.contextMenu.templateForElement(element)[2]).toBeUndefined()
|
expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 1"
|
||||||
|
expect(atom.contextMenu.templateForElement(element)[2]).toBeUndefined()
|
||||||
|
|
||||||
describe "stylesheet loading", ->
|
describe "stylesheet loading", ->
|
||||||
describe "when the metadata contains a 'stylesheets' manifest", ->
|
describe "when the metadata contains a 'stylesheets' manifest", ->
|
||||||
@@ -270,12 +281,14 @@ describe "PackageManager", ->
|
|||||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||||
|
|
||||||
atom.packages.activatePackage("package-with-stylesheets-manifest")
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage("package-with-stylesheets-manifest")
|
||||||
|
|
||||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
runs ->
|
||||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||||
expect($('#jasmine-content').css('font-size')).toBe '1px'
|
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||||
|
expect($('#jasmine-content').css('font-size')).toBe '1px'
|
||||||
|
|
||||||
describe "when the metadata does not contain a 'stylesheets' manifest", ->
|
describe "when the metadata does not contain a 'stylesheets' manifest", ->
|
||||||
it "loads all stylesheets from the stylesheets directory", ->
|
it "loads all stylesheets from the stylesheets directory", ->
|
||||||
@@ -292,11 +305,22 @@ describe "PackageManager", ->
|
|||||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||||
|
|
||||||
|
waitsForPromise ->
|
||||||
|
atom.packages.activatePackage("package-with-stylesheets")
|
||||||
|
|
||||||
|
runs ->
|
||||||
|
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||||
|
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||||
|
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
|
||||||
|
expect($('#jasmine-content').css('font-size')).toBe '3px'
|
||||||
|
|
||||||
|
it "assigns the stylesheet's context based on the filename", ->
|
||||||
|
waitsForPromise ->
|
||||||
atom.packages.activatePackage("package-with-stylesheets")
|
atom.packages.activatePackage("package-with-stylesheets")
|
||||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
|
||||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
runs ->
|
||||||
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
|
element = atom.styles.getStyleElements().find (element) -> element.context is 'test-context'
|
||||||
expect($('#jasmine-content').css('font-size')).toBe '3px'
|
expect(element).toBeDefined()
|
||||||
|
|
||||||
describe "grammar loading", ->
|
describe "grammar loading", ->
|
||||||
it "loads the package's grammars", ->
|
it "loads the package's grammars", ->
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ describe "Package", ->
|
|||||||
|
|
||||||
it "reloads without readding to the stylesheets list", ->
|
it "reloads without readding to the stylesheets list", ->
|
||||||
expect(theme.getStylesheetPaths().length).toBe 3
|
expect(theme.getStylesheetPaths().length).toBe 3
|
||||||
theme.reloadStylesheet(theme.getStylesheetPaths()[0])
|
theme.reloadStylesheets()
|
||||||
expect(theme.getStylesheetPaths().length).toBe 3
|
expect(theme.getStylesheetPaths().length).toBe 3
|
||||||
|
|
||||||
describe "events", ->
|
describe "events", ->
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ describe "SelectListView", ->
|
|||||||
describe "when the mini editor loses focus", ->
|
describe "when the mini editor loses focus", ->
|
||||||
it "triggers the cancelled hook and detaches the select list", ->
|
it "triggers the cancelled hook and detaches the select list", ->
|
||||||
spyOn(selectList, 'detach')
|
spyOn(selectList, 'detach')
|
||||||
filterEditorView.hiddenInput.trigger 'focusout'
|
filterEditorView.trigger 'blur'
|
||||||
expect(selectList.cancelled).toHaveBeenCalled()
|
expect(selectList.cancelled).toHaveBeenCalled()
|
||||||
expect(selectList.detach).toHaveBeenCalled()
|
expect(selectList.detach).toHaveBeenCalled()
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ beforeEach ->
|
|||||||
config.set "editor.autoIndent", false
|
config.set "editor.autoIndent", false
|
||||||
config.set "core.disabledPackages", ["package-that-throws-an-exception",
|
config.set "core.disabledPackages", ["package-that-throws-an-exception",
|
||||||
"package-with-broken-package-json", "package-with-broken-keymap"]
|
"package-with-broken-package-json", "package-with-broken-keymap"]
|
||||||
|
config.set "editor.useShadowDOM", true
|
||||||
config.load.reset()
|
config.load.reset()
|
||||||
config.save.reset()
|
config.save.reset()
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ addCustomMatchers = (spec) ->
|
|||||||
@message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus."
|
@message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus."
|
||||||
element = @actual
|
element = @actual
|
||||||
element = element.get(0) if element.jquery
|
element = element.get(0) if element.jquery
|
||||||
element.webkitMatchesSelector(":focus") or element.querySelector(":focus")
|
element is document.activeElement or element.contains(document.activeElement)
|
||||||
|
|
||||||
toShow: ->
|
toShow: ->
|
||||||
notText = if @isNot then " not" else ""
|
notText = if @isNot then " not" else ""
|
||||||
@@ -338,12 +339,8 @@ window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.c
|
|||||||
$(window).trigger 'resize' # update width of editor view's on-screen lines
|
$(window).trigger 'resize' # update width of editor view's on-screen lines
|
||||||
|
|
||||||
window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) ->
|
window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) ->
|
||||||
if editorView.hasClass('react')
|
editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines)
|
||||||
editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines)
|
editorView.component?.measureHeightAndWidth()
|
||||||
editorView.component?.measureHeightAndWidth()
|
|
||||||
else
|
|
||||||
editorView.height(lineHeight * heightInLines + editorView.renderedLines.position().top)
|
|
||||||
$(window).trigger 'resize' # update editor view's on-screen lines
|
|
||||||
|
|
||||||
$.fn.resultOfTrigger = (type) ->
|
$.fn.resultOfTrigger = (type) ->
|
||||||
event = $.Event(type)
|
event = $.Event(type)
|
||||||
|
|||||||
@@ -52,3 +52,24 @@ describe "StylesElement", ->
|
|||||||
expect(element.children.length).toBe initialChildCount + 1
|
expect(element.children.length).toBe initialChildCount + 1
|
||||||
expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}"
|
expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}"
|
||||||
expect(updatedStyleElements).toEqual [element.children[initialChildCount]]
|
expect(updatedStyleElements).toEqual [element.children[initialChildCount]]
|
||||||
|
|
||||||
|
it "only includes style elements matching the 'context' attribute", ->
|
||||||
|
initialChildCount = element.children.length
|
||||||
|
|
||||||
|
atom.styles.addStyleSheet("a {color: red;}", context: 'test-context')
|
||||||
|
atom.styles.addStyleSheet("a {color: green;}")
|
||||||
|
|
||||||
|
expect(element.children.length).toBe initialChildCount + 1
|
||||||
|
expect(element.children[initialChildCount].textContent).toBe "a {color: green;}"
|
||||||
|
|
||||||
|
element.setAttribute('context', 'test-context')
|
||||||
|
|
||||||
|
expect(element.children.length).toBe 1
|
||||||
|
expect(element.children[0].textContent).toBe "a {color: red;}"
|
||||||
|
|
||||||
|
atom.styles.addStyleSheet("a {color: blue;}", context: 'test-context')
|
||||||
|
atom.styles.addStyleSheet("a {color: yellow;}")
|
||||||
|
|
||||||
|
expect(element.children.length).toBe 2
|
||||||
|
expect(element.children[0].textContent).toBe "a {color: red;}"
|
||||||
|
expect(element.children[1].textContent).toBe "a {color: blue;}"
|
||||||
|
|||||||
@@ -721,11 +721,11 @@ describe "TextEditorComponent", ->
|
|||||||
editor.setCursorScreenPosition([0, 16])
|
editor.setCursorScreenPosition([0, 16])
|
||||||
nextAnimationFrame()
|
nextAnimationFrame()
|
||||||
|
|
||||||
atom.themes.applyStylesheet 'test', """
|
atom.styles.addStyleSheet """
|
||||||
.function.js {
|
.function.js {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
"""
|
""", context: 'atom-text-editor'
|
||||||
nextAnimationFrame() # update based on new measurements
|
nextAnimationFrame() # update based on new measurements
|
||||||
|
|
||||||
cursor = componentNode.querySelector('.cursor')
|
cursor = componentNode.querySelector('.cursor')
|
||||||
@@ -1537,8 +1537,9 @@ describe "TextEditorComponent", ->
|
|||||||
|
|
||||||
it "transfers focus to the hidden input", ->
|
it "transfers focus to the hidden input", ->
|
||||||
expect(document.activeElement).toBe document.body
|
expect(document.activeElement).toBe document.body
|
||||||
componentNode.focus()
|
wrapperNode.focus()
|
||||||
expect(document.activeElement).toBe inputNode
|
expect(document.activeElement).toBe wrapperNode
|
||||||
|
expect(wrapperNode.shadowRoot.activeElement).toBe inputNode
|
||||||
|
|
||||||
it "adds the 'is-focused' class to the editor when the hidden input is focused", ->
|
it "adds the 'is-focused' class to the editor when the hidden input is focused", ->
|
||||||
expect(document.activeElement).toBe document.body
|
expect(document.activeElement).toBe document.body
|
||||||
@@ -1667,12 +1668,13 @@ describe "TextEditorComponent", ->
|
|||||||
component.measureHeightAndWidth()
|
component.measureHeightAndWidth()
|
||||||
nextAnimationFrame()
|
nextAnimationFrame()
|
||||||
|
|
||||||
atom.themes.applyStylesheet "test", """
|
atom.styles.addStyleSheet """
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
}
|
}
|
||||||
"""
|
""", context: 'atom-text-editor'
|
||||||
|
|
||||||
nextAnimationFrame()
|
nextAnimationFrame()
|
||||||
|
|
||||||
scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner')
|
scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner')
|
||||||
|
|||||||
@@ -19,18 +19,43 @@ describe "TextEditorElement", ->
|
|||||||
element = jasmineContent.firstChild
|
element = jasmineContent.firstChild
|
||||||
expect(element.getModel().getPlaceholderText()).toBe 'testing'
|
expect(element.getModel().getPlaceholderText()).toBe 'testing'
|
||||||
|
|
||||||
describe "::focus()", ->
|
describe "focus and blur handling", ->
|
||||||
it "transfers focus to the hidden text area and does not emit 'focusout' or 'blur' events", ->
|
describe "when the editor.useShadowDOM config option is true", ->
|
||||||
element = new TextEditorElement
|
it "proxies focus/blur events to/from the hidden input inside the shadow root", ->
|
||||||
jasmineContent.appendChild(element)
|
atom.config.set('editor.useShadowDOM', true)
|
||||||
|
|
||||||
focusoutCalled = false
|
element = new TextEditorElement
|
||||||
element.addEventListener 'focusout', -> focusoutCalled = true
|
jasmineContent.appendChild(element)
|
||||||
blurCalled = false
|
|
||||||
element.addEventListener 'blur', -> blurCalled = true
|
|
||||||
|
|
||||||
element.focus()
|
blurCalled = false
|
||||||
expect(focusoutCalled).toBe false
|
element.addEventListener 'blur', -> blurCalled = true
|
||||||
expect(blurCalled).toBe false
|
|
||||||
expect(element.hasFocus()).toBe true
|
element.focus()
|
||||||
expect(element.querySelector('input')).toBe document.activeElement
|
expect(blurCalled).toBe false
|
||||||
|
expect(element.hasFocus()).toBe true
|
||||||
|
expect(document.activeElement).toBe element
|
||||||
|
expect(element.shadowRoot.activeElement).toBe element.shadowRoot.querySelector('input')
|
||||||
|
|
||||||
|
document.body.focus()
|
||||||
|
expect(blurCalled).toBe true
|
||||||
|
|
||||||
|
describe "when the editor.useShadowDOM config option is false", ->
|
||||||
|
afterEach ->
|
||||||
|
document.head.querySelector('atom-styles[context="atom-text-editor"]').remove()
|
||||||
|
|
||||||
|
it "proxies focus/blur events to/from the hidden input", ->
|
||||||
|
atom.config.set('editor.useShadowDOM', false)
|
||||||
|
|
||||||
|
element = new TextEditorElement
|
||||||
|
jasmineContent.appendChild(element)
|
||||||
|
|
||||||
|
blurCalled = false
|
||||||
|
element.addEventListener 'blur', -> blurCalled = true
|
||||||
|
|
||||||
|
element.focus()
|
||||||
|
expect(blurCalled).toBe false
|
||||||
|
expect(element.hasFocus()).toBe true
|
||||||
|
expect(document.activeElement).toBe element.querySelector('input')
|
||||||
|
|
||||||
|
document.body.focus()
|
||||||
|
expect(blurCalled).toBe true
|
||||||
|
|||||||
@@ -85,7 +85,16 @@ describe "ThemeManager", ->
|
|||||||
runs ->
|
runs ->
|
||||||
reloadHandler.reset()
|
reloadHandler.reset()
|
||||||
expect($('style.theme')).toHaveLength 0
|
expect($('style.theme')).toHaveLength 0
|
||||||
atom.config.set('core.themes', ['atom-dark-syntax'])
|
atom.config.set('core.themes', ['atom-dark-ui'])
|
||||||
|
|
||||||
|
waitsFor ->
|
||||||
|
reloadHandler.callCount == 1
|
||||||
|
|
||||||
|
runs ->
|
||||||
|
reloadHandler.reset()
|
||||||
|
expect($('style[group=theme]')).toHaveLength 1
|
||||||
|
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
|
||||||
|
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui'])
|
||||||
|
|
||||||
waitsFor ->
|
waitsFor ->
|
||||||
reloadHandler.callCount == 1
|
reloadHandler.callCount == 1
|
||||||
@@ -93,17 +102,8 @@ describe "ThemeManager", ->
|
|||||||
runs ->
|
runs ->
|
||||||
reloadHandler.reset()
|
reloadHandler.reset()
|
||||||
expect($('style[group=theme]')).toHaveLength 2
|
expect($('style[group=theme]')).toHaveLength 2
|
||||||
expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-dark-syntax/
|
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
|
||||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-light-ui/
|
||||||
|
|
||||||
waitsFor ->
|
|
||||||
reloadHandler.callCount == 1
|
|
||||||
|
|
||||||
runs ->
|
|
||||||
reloadHandler.reset()
|
|
||||||
expect($('style[group=theme]')).toHaveLength 2
|
|
||||||
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-syntax/
|
|
||||||
expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-light-syntax/
|
|
||||||
atom.config.set('core.themes', [])
|
atom.config.set('core.themes', [])
|
||||||
|
|
||||||
waitsFor ->
|
waitsFor ->
|
||||||
@@ -111,7 +111,7 @@ describe "ThemeManager", ->
|
|||||||
|
|
||||||
runs ->
|
runs ->
|
||||||
reloadHandler.reset()
|
reloadHandler.reset()
|
||||||
expect($('style[group=theme]')).toHaveLength 2
|
expect($('style[group=theme]')).toHaveLength 1
|
||||||
# atom-dark-ui has an directory path, the syntax one doesn't
|
# atom-dark-ui has an directory path, the syntax one doesn't
|
||||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ module.exports =
|
|||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
default: true
|
default: true
|
||||||
description: 'Disabling will improve editor font rendering but reduce scrolling performance.'
|
description: 'Disabling will improve editor font rendering but reduce scrolling performance.'
|
||||||
|
useShadowDOM:
|
||||||
|
type: 'boolean'
|
||||||
|
default: false
|
||||||
|
title: 'Use Shadow DOM'
|
||||||
|
description: 'Enable to test out themes and packages with the new shadow DOM before it ships by default.'
|
||||||
confirmCheckoutHeadRevision:
|
confirmCheckoutHeadRevision:
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
default: true
|
default: true
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ GutterComponent = React.createClass
|
|||||||
measuredWidth: null
|
measuredWidth: null
|
||||||
|
|
||||||
render: ->
|
render: ->
|
||||||
{scrollHeight, scrollViewHeight, onMouseDown, backgroundColor, gutterBackgroundColor} = @props
|
{scrollHeight, scrollViewHeight, backgroundColor, gutterBackgroundColor} = @props
|
||||||
|
|
||||||
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
|
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
|
||||||
backgroundColor = gutterBackgroundColor
|
backgroundColor = gutterBackgroundColor
|
||||||
|
|
||||||
div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown,
|
div className: 'gutter',
|
||||||
div className: 'line-numbers', ref: 'lineNumbers', style:
|
div className: 'line-numbers', ref: 'lineNumbers', style:
|
||||||
height: Math.max(scrollHeight, scrollViewHeight)
|
height: Math.max(scrollHeight, scrollViewHeight)
|
||||||
WebkitTransform: @getTransform()
|
WebkitTransform: @getTransform()
|
||||||
@@ -45,6 +45,10 @@ GutterComponent = React.createClass
|
|||||||
@appendDummyLineNumber()
|
@appendDummyLineNumber()
|
||||||
@updateLineNumbers() if @props.performedInitialMeasurement
|
@updateLineNumbers() if @props.performedInitialMeasurement
|
||||||
|
|
||||||
|
node = @getDOMNode()
|
||||||
|
node.addEventListener 'click', @onClick
|
||||||
|
node.addEventListener 'mousedown', @onMouseDown
|
||||||
|
|
||||||
# Only update the gutter if the visible row range has changed or if a
|
# Only update the gutter if the visible row range has changed or if a
|
||||||
# non-zero-delta change to the screen lines has occurred within the current
|
# non-zero-delta change to the screen lines has occurred within the current
|
||||||
# visible row range.
|
# visible row range.
|
||||||
|
|||||||
@@ -21,5 +21,11 @@ HighlightsComponent = React.createClass
|
|||||||
|
|
||||||
highlightComponents
|
highlightComponents
|
||||||
|
|
||||||
|
componentDidMount: ->
|
||||||
|
if atom.config.get('editor.useShadowDOM')
|
||||||
|
insertionPoint = document.createElement('content')
|
||||||
|
insertionPoint.setAttribute('select', '.underlayer')
|
||||||
|
@getDOMNode().appendChild(insertionPoint)
|
||||||
|
|
||||||
shouldComponentUpdate: (newProps) ->
|
shouldComponentUpdate: (newProps) ->
|
||||||
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount')
|
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount')
|
||||||
|
|||||||
@@ -7,16 +7,17 @@ InputComponent = React.createClass
|
|||||||
displayName: 'InputComponent'
|
displayName: 'InputComponent'
|
||||||
|
|
||||||
render: ->
|
render: ->
|
||||||
{className, style, onFocus, onBlur} = @props
|
{className, style} = @props
|
||||||
|
|
||||||
input {className, style, onFocus, onBlur, 'data-react-skip-selection-restoration': true}
|
input {className, style, 'data-react-skip-selection-restoration': true}
|
||||||
|
|
||||||
getInitialState: ->
|
getInitialState: ->
|
||||||
{lastChar: ''}
|
{lastChar: ''}
|
||||||
|
|
||||||
componentDidMount: ->
|
componentDidMount: ->
|
||||||
@getDOMNode().addEventListener 'paste', @onPaste
|
node = @getDOMNode()
|
||||||
@getDOMNode().addEventListener 'compositionupdate', @onCompositionUpdate
|
node.addEventListener 'paste', @onPaste
|
||||||
|
node.addEventListener 'compositionupdate', @onCompositionUpdate
|
||||||
|
|
||||||
# Don't let text accumulate in the input forever, but avoid excessive reflows
|
# Don't let text accumulate in the input forever, but avoid excessive reflows
|
||||||
componentDidUpdate: ->
|
componentDidUpdate: ->
|
||||||
@@ -34,11 +35,5 @@ InputComponent = React.createClass
|
|||||||
onPaste: (e) ->
|
onPaste: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
onFocus: ->
|
|
||||||
@props.onFocus?()
|
|
||||||
|
|
||||||
onBlur: ->
|
|
||||||
@props.onBlur?()
|
|
||||||
|
|
||||||
focus: ->
|
focus: ->
|
||||||
@getDOMNode().focus()
|
@getDOMNode().focus()
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ LinesComponent = React.createClass
|
|||||||
@lineIdsByScreenRow = {}
|
@lineIdsByScreenRow = {}
|
||||||
@renderedDecorationsByLineId = {}
|
@renderedDecorationsByLineId = {}
|
||||||
|
|
||||||
|
componentDidMount: ->
|
||||||
|
if atom.config.get('editor.useShadowDOM')
|
||||||
|
insertionPoint = document.createElement('content')
|
||||||
|
insertionPoint.setAttribute('select', '.overlayer')
|
||||||
|
@getDOMNode().appendChild(insertionPoint)
|
||||||
|
|
||||||
shouldComponentUpdate: (newProps) ->
|
shouldComponentUpdate: (newProps) ->
|
||||||
return true unless isEqualForProperties(newProps, @props,
|
return true unless isEqualForProperties(newProps, @props,
|
||||||
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
|
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
|
||||||
|
|||||||
+16
-9
@@ -50,6 +50,7 @@ class Package
|
|||||||
keymaps: null
|
keymaps: null
|
||||||
menus: null
|
menus: null
|
||||||
stylesheets: null
|
stylesheets: null
|
||||||
|
stylesheetDisposables: null
|
||||||
grammars: null
|
grammars: null
|
||||||
scopedProperties: null
|
scopedProperties: null
|
||||||
mainModulePath: null
|
mainModulePath: null
|
||||||
@@ -175,9 +176,16 @@ class Package
|
|||||||
activateStylesheets: ->
|
activateStylesheets: ->
|
||||||
return if @stylesheetsActivated
|
return if @stylesheetsActivated
|
||||||
|
|
||||||
type = @getStylesheetType()
|
|
||||||
for [stylesheetPath, content] in @stylesheets
|
group = @getStylesheetType()
|
||||||
atom.themes.applyStylesheet(stylesheetPath, content, type)
|
@stylesheetDisposables = new CompositeDisposable
|
||||||
|
for [sourcePath, source] in @stylesheets
|
||||||
|
if match = path.basename(sourcePath).match(/[^.]*\.([^.]*)\./)
|
||||||
|
context = match[1]
|
||||||
|
else if @metadata.theme is 'syntax'
|
||||||
|
context = 'atom-text-editor'
|
||||||
|
|
||||||
|
@stylesheetDisposables.add(atom.styles.addStyleSheet(source, {sourcePath, group, context}))
|
||||||
@stylesheetsActivated = true
|
@stylesheetsActivated = true
|
||||||
|
|
||||||
activateResources: ->
|
activateResources: ->
|
||||||
@@ -320,7 +328,7 @@ class Package
|
|||||||
deactivateResources: ->
|
deactivateResources: ->
|
||||||
grammar.deactivate() for grammar in @grammars
|
grammar.deactivate() for grammar in @grammars
|
||||||
scopedProperties.deactivate() for scopedProperties in @scopedProperties
|
scopedProperties.deactivate() for scopedProperties in @scopedProperties
|
||||||
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets
|
@stylesheetDisposables?.dispose()
|
||||||
@activationDisposables?.dispose()
|
@activationDisposables?.dispose()
|
||||||
@stylesheetsActivated = false
|
@stylesheetsActivated = false
|
||||||
@grammarsActivated = false
|
@grammarsActivated = false
|
||||||
@@ -329,11 +337,10 @@ class Package
|
|||||||
reloadStylesheets: ->
|
reloadStylesheets: ->
|
||||||
oldSheets = _.clone(@stylesheets)
|
oldSheets = _.clone(@stylesheets)
|
||||||
@loadStylesheets()
|
@loadStylesheets()
|
||||||
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in oldSheets
|
@stylesheetDisposables.dispose()
|
||||||
@reloadStylesheet(stylesheetPath, content) for [stylesheetPath, content] in @stylesheets
|
@stylesheetDisposables = new CompositeDisposable
|
||||||
|
@stylesheetsActivated = false
|
||||||
reloadStylesheet: (stylesheetPath, content) ->
|
@activateStylesheets()
|
||||||
atom.themes.applyStylesheet(stylesheetPath, content, @getStylesheetType())
|
|
||||||
|
|
||||||
requireMainModule: ->
|
requireMainModule: ->
|
||||||
return @mainModule if @mainModule?
|
return @mainModule if @mainModule?
|
||||||
|
|||||||
@@ -28,9 +28,17 @@ class PaneElement extends HTMLElement
|
|||||||
@itemViews.setAttribute 'class', 'item-views'
|
@itemViews.setAttribute 'class', 'item-views'
|
||||||
|
|
||||||
subscribeToDOMEvents: ->
|
subscribeToDOMEvents: ->
|
||||||
@addEventListener 'focusin', => @model.focus()
|
handleFocus = (event) =>
|
||||||
@addEventListener 'focusout', => @model.blur()
|
@model.focus()
|
||||||
@addEventListener 'focus', => @getActiveView()?.focus()
|
if event.target is this and view = @getActiveView()
|
||||||
|
view.focus()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
handleBlur = (event) =>
|
||||||
|
@model.blur() unless @contains(event.relatedTarget)
|
||||||
|
|
||||||
|
@addEventListener 'focus', handleFocus, true
|
||||||
|
@addEventListener 'blur', handleBlur, true
|
||||||
|
|
||||||
createSpacePenShim: ->
|
createSpacePenShim: ->
|
||||||
@__spacePenView = new PaneView(this)
|
@__spacePenView = new PaneView(this)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ ScrollbarComponent = React.createClass
|
|||||||
style.right = verticalScrollbarWidth if scrollableInOppositeDirection
|
style.right = verticalScrollbarWidth if scrollableInOppositeDirection
|
||||||
style.height = horizontalScrollbarHeight
|
style.height = horizontalScrollbarHeight
|
||||||
|
|
||||||
div {className, style, @onScroll},
|
div {className, style},
|
||||||
switch orientation
|
switch orientation
|
||||||
when 'vertical'
|
when 'vertical'
|
||||||
div className: 'scrollbar-content', style: {height: scrollHeight}
|
div className: 'scrollbar-content', style: {height: scrollHeight}
|
||||||
@@ -36,6 +36,11 @@ ScrollbarComponent = React.createClass
|
|||||||
unless orientation is 'vertical' or orientation is 'horizontal'
|
unless orientation is 'vertical' or orientation is 'horizontal'
|
||||||
throw new Error("Must specify an orientation property of 'vertical' or 'horizontal'")
|
throw new Error("Must specify an orientation property of 'vertical' or 'horizontal'")
|
||||||
|
|
||||||
|
@getDOMNode().addEventListener 'scroll', @onScroll
|
||||||
|
|
||||||
|
componentWillUnmount: ->
|
||||||
|
@getDOMNode().removeEventListener 'scroll', @onScroll
|
||||||
|
|
||||||
shouldComponentUpdate: (newProps) ->
|
shouldComponentUpdate: (newProps) ->
|
||||||
return true if newProps.visible isnt @props.visible
|
return true if newProps.visible isnt @props.visible
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class SelectListView extends View
|
|||||||
initialize: ->
|
initialize: ->
|
||||||
@filterEditorView.getEditor().getBuffer().onDidChange =>
|
@filterEditorView.getEditor().getBuffer().onDidChange =>
|
||||||
@schedulePopulateList()
|
@schedulePopulateList()
|
||||||
@filterEditorView.hiddenInput.on 'focusout', =>
|
@filterEditorView.on 'blur', =>
|
||||||
@cancel() unless @cancelling
|
@cancel() unless @cancelling
|
||||||
|
|
||||||
# This prevents the focusout event from firing on the filter editor view
|
# This prevents the focusout event from firing on the filter editor view
|
||||||
@@ -254,7 +254,7 @@ class SelectListView extends View
|
|||||||
# Extended: Store the currently focused element. This element will be given
|
# Extended: Store the currently focused element. This element will be given
|
||||||
# back focus when {::cancel} is called.
|
# back focus when {::cancel} is called.
|
||||||
storeFocusedElement: ->
|
storeFocusedElement: ->
|
||||||
@previouslyFocusedElement = $(':focus')
|
@previouslyFocusedElement = $(document.activeElement)
|
||||||
|
|
||||||
###
|
###
|
||||||
Section: Private
|
Section: Private
|
||||||
|
|||||||
@@ -81,6 +81,18 @@ jQuery.event.remove = (elem, types, originalHandler, selector, mappedTypes) ->
|
|||||||
handler = HandlersByOriginalHandler.get(originalHandler) ? originalHandler
|
handler = HandlersByOriginalHandler.get(originalHandler) ? originalHandler
|
||||||
JQueryEventRemove(elem, types, handler, selector, mappedTypes, RemoveEventListener if atom?.commands?)
|
JQueryEventRemove(elem, types, handler, selector, mappedTypes, RemoveEventListener if atom?.commands?)
|
||||||
|
|
||||||
|
JQueryContains = jQuery.contains
|
||||||
|
|
||||||
|
jQuery.contains = (a, b) ->
|
||||||
|
shadowRoot = null
|
||||||
|
currentNode = b
|
||||||
|
while currentNode
|
||||||
|
if currentNode instanceof ShadowRoot and a.contains(currentNode.host)
|
||||||
|
return true
|
||||||
|
currentNode = currentNode.parentNode
|
||||||
|
|
||||||
|
JQueryContains.call(this, a, b)
|
||||||
|
|
||||||
tooltipDefaults =
|
tooltipDefaults =
|
||||||
delay:
|
delay:
|
||||||
show: 1000
|
show: 1000
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class StyleManager
|
|||||||
|
|
||||||
addStyleSheet: (source, params) ->
|
addStyleSheet: (source, params) ->
|
||||||
sourcePath = params?.sourcePath
|
sourcePath = params?.sourcePath
|
||||||
|
context = params?.context
|
||||||
group = params?.group
|
group = params?.group
|
||||||
|
|
||||||
if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath]
|
if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||||
|
|
||||||
class StylesElement extends HTMLElement
|
class StylesElement extends HTMLElement
|
||||||
createdCallback: ->
|
subscriptions: null
|
||||||
@emitter = new Emitter
|
context: null
|
||||||
|
|
||||||
onDidAddStyleElement: (callback) ->
|
onDidAddStyleElement: (callback) ->
|
||||||
@emitter.on 'did-add-style-element', callback
|
@emitter.on 'did-add-style-element', callback
|
||||||
@@ -13,15 +13,42 @@ class StylesElement extends HTMLElement
|
|||||||
onDidUpdateStyleElement: (callback) ->
|
onDidUpdateStyleElement: (callback) ->
|
||||||
@emitter.on 'did-update-style-element', callback
|
@emitter.on 'did-update-style-element', callback
|
||||||
|
|
||||||
attachedCallback: ->
|
createdCallback: ->
|
||||||
@subscriptions = new CompositeDisposable
|
@emitter = new Emitter
|
||||||
@styleElementClonesByOriginalElement = new WeakMap
|
@styleElementClonesByOriginalElement = new WeakMap
|
||||||
|
|
||||||
|
attachedCallback: ->
|
||||||
|
@initialize()
|
||||||
|
|
||||||
|
detachedCallback: ->
|
||||||
|
@subscriptions.dispose()
|
||||||
|
@subscriptions = null
|
||||||
|
|
||||||
|
attributeChangedCallback: (attrName, oldVal, newVal) ->
|
||||||
|
@contextChanged() if attrName is 'context'
|
||||||
|
|
||||||
|
initialize: ->
|
||||||
|
return if @subscriptions?
|
||||||
|
|
||||||
|
@subscriptions = new CompositeDisposable
|
||||||
|
@context = @getAttribute('context') ? undefined
|
||||||
|
|
||||||
@subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this))
|
@subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this))
|
||||||
@subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
|
@subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
|
||||||
@subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
|
@subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
|
||||||
|
|
||||||
|
contextChanged: ->
|
||||||
|
return unless @subscriptions?
|
||||||
|
|
||||||
|
@styleElementRemoved(child) for child in Array::slice.call(@children)
|
||||||
|
@context = @getAttribute('context')
|
||||||
|
@styleElementAdded(styleElement) for styleElement in atom.styles.getStyleElements()
|
||||||
|
|
||||||
styleElementAdded: (styleElement) ->
|
styleElementAdded: (styleElement) ->
|
||||||
|
return unless styleElement.context is @context
|
||||||
|
|
||||||
styleElementClone = styleElement.cloneNode(true)
|
styleElementClone = styleElement.cloneNode(true)
|
||||||
|
styleElementClone.context = styleElement.context
|
||||||
@styleElementClonesByOriginalElement.set(styleElement, styleElementClone)
|
@styleElementClonesByOriginalElement.set(styleElement, styleElementClone)
|
||||||
|
|
||||||
group = styleElement.getAttribute('group')
|
group = styleElement.getAttribute('group')
|
||||||
@@ -35,16 +62,17 @@ class StylesElement extends HTMLElement
|
|||||||
@emitter.emit 'did-add-style-element', styleElementClone
|
@emitter.emit 'did-add-style-element', styleElementClone
|
||||||
|
|
||||||
styleElementRemoved: (styleElement) ->
|
styleElementRemoved: (styleElement) ->
|
||||||
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement)
|
return unless styleElement.context is @context
|
||||||
|
|
||||||
|
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement) ? styleElement
|
||||||
styleElementClone.remove()
|
styleElementClone.remove()
|
||||||
@emitter.emit 'did-remove-style-element', styleElementClone
|
@emitter.emit 'did-remove-style-element', styleElementClone
|
||||||
|
|
||||||
styleElementUpdated: (styleElement) ->
|
styleElementUpdated: (styleElement) ->
|
||||||
|
return unless styleElement.context is @context
|
||||||
|
|
||||||
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement)
|
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement)
|
||||||
styleElementClone.textContent = styleElement.textContent
|
styleElementClone.textContent = styleElement.textContent
|
||||||
@emitter.emit 'did-update-style-element', styleElementClone
|
@emitter.emit 'did-update-style-element', styleElementClone
|
||||||
|
|
||||||
detachedCallback: ->
|
|
||||||
@subscriptions.dispose()
|
|
||||||
|
|
||||||
module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype
|
module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ TextEditorComponent = React.createClass
|
|||||||
className += ' is-focused' if focused
|
className += ' is-focused' if focused
|
||||||
className += ' has-selection' if hasSelection
|
className += ' has-selection' if hasSelection
|
||||||
|
|
||||||
div {className, style, tabIndex: -1},
|
div {className, style},
|
||||||
if @shouldRenderGutter()
|
if @shouldRenderGutter()
|
||||||
GutterComponent {
|
GutterComponent {
|
||||||
ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations,
|
ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations,
|
||||||
@@ -102,13 +102,11 @@ TextEditorComponent = React.createClass
|
|||||||
@useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor
|
@useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown,
|
div ref: 'scrollView', className: 'scroll-view',
|
||||||
InputComponent
|
InputComponent
|
||||||
ref: 'input'
|
ref: 'input'
|
||||||
className: 'hidden-input'
|
className: 'hidden-input'
|
||||||
style: hiddenInputStyle
|
style: hiddenInputStyle
|
||||||
onFocus: @onInputFocused
|
|
||||||
onBlur: @onInputBlurred
|
|
||||||
|
|
||||||
LinesComponent {
|
LinesComponent {
|
||||||
ref: 'lines',
|
ref: 'lines',
|
||||||
@@ -175,14 +173,14 @@ TextEditorComponent = React.createClass
|
|||||||
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
|
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
|
||||||
|
|
||||||
componentDidMount: ->
|
componentDidMount: ->
|
||||||
{editor} = @props
|
{editor, stylesElement} = @props
|
||||||
|
|
||||||
@observeEditor()
|
@observeEditor()
|
||||||
@listenForDOMEvents()
|
@listenForDOMEvents()
|
||||||
|
|
||||||
@subscribe atom.themes.onDidAddStylesheet @onStylesheetsChanged
|
@subscribe stylesElement.onDidAddStyleElement @onStylesheetsChanged
|
||||||
@subscribe atom.themes.onDidUpdateStylesheet @onStylesheetsChanged
|
@subscribe stylesElement.onDidUpdateStyleElement @onStylesheetsChanged
|
||||||
@subscribe atom.themes.onDidRemoveStylesheet @onStylesheetsChanged
|
@subscribe stylesElement.onDidRemoveStyleElement @onStylesheetsChanged
|
||||||
unless atom.themes.isInitialLoadComplete()
|
unless atom.themes.isInitialLoadComplete()
|
||||||
@subscribe atom.themes.onDidReloadAll @onStylesheetsChanged
|
@subscribe atom.themes.onDidReloadAll @onStylesheetsChanged
|
||||||
@subscribe scrollbarStyle.changes, @refreshScrollbars
|
@subscribe scrollbarStyle.changes, @refreshScrollbars
|
||||||
@@ -193,9 +191,9 @@ TextEditorComponent = React.createClass
|
|||||||
@checkForVisibilityChange()
|
@checkForVisibilityChange()
|
||||||
|
|
||||||
componentWillUnmount: ->
|
componentWillUnmount: ->
|
||||||
{editor, parentView} = @props
|
{editor, hostElement} = @props
|
||||||
|
|
||||||
parentView.__spacePenView.trigger 'editor:will-be-removed', [parentView.__spacePenView]
|
hostElement.__spacePenView.trigger 'editor:will-be-removed', [hostElement.__spacePenView]
|
||||||
@unsubscribe()
|
@unsubscribe()
|
||||||
@scopedConfigSubscriptions.dispose()
|
@scopedConfigSubscriptions.dispose()
|
||||||
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
|
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
|
||||||
@@ -215,9 +213,9 @@ TextEditorComponent = React.createClass
|
|||||||
if @props.editor.isAlive()
|
if @props.editor.isAlive()
|
||||||
@updateParentViewFocusedClassIfNeeded(prevState)
|
@updateParentViewFocusedClassIfNeeded(prevState)
|
||||||
@updateParentViewMiniClassIfNeeded(prevState)
|
@updateParentViewMiniClassIfNeeded(prevState)
|
||||||
@props.parentView.__spacePenView.trigger 'cursor:moved' if cursorMoved
|
@props.hostElement.__spacePenView.trigger 'cursor:moved' if cursorMoved
|
||||||
@props.parentView.__spacePenView.trigger 'selection:changed' if selectionChanged
|
@props.hostElement.__spacePenView.trigger 'selection:changed' if selectionChanged
|
||||||
@props.parentView.__spacePenView.trigger 'editor:display-updated'
|
@props.hostElement.__spacePenView.trigger 'editor:display-updated'
|
||||||
|
|
||||||
becameVisible: ->
|
becameVisible: ->
|
||||||
@updatesPaused = true
|
@updatesPaused = true
|
||||||
@@ -261,7 +259,7 @@ TextEditorComponent = React.createClass
|
|||||||
@forceUpdate()
|
@forceUpdate()
|
||||||
|
|
||||||
getTopmostDOMNode: ->
|
getTopmostDOMNode: ->
|
||||||
@props.parentView
|
@props.hostElement
|
||||||
|
|
||||||
getRenderedRowRange: ->
|
getRenderedRowRange: ->
|
||||||
{editor, lineOverdrawMargin} = @props
|
{editor, lineOverdrawMargin} = @props
|
||||||
@@ -378,8 +376,8 @@ TextEditorComponent = React.createClass
|
|||||||
listenForDOMEvents: ->
|
listenForDOMEvents: ->
|
||||||
node = @getDOMNode()
|
node = @getDOMNode()
|
||||||
node.addEventListener 'mousewheel', @onMouseWheel
|
node.addEventListener 'mousewheel', @onMouseWheel
|
||||||
node.addEventListener 'focus', @onFocus # For some reason, React's built in focus events seem to bubble
|
|
||||||
node.addEventListener 'textInput', @onTextInput
|
node.addEventListener 'textInput', @onTextInput
|
||||||
|
@refs.scrollView.getDOMNode().addEventListener 'mousedown', @onMouseDown
|
||||||
|
|
||||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||||
scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
|
scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
|
||||||
@@ -415,6 +413,9 @@ TextEditorComponent = React.createClass
|
|||||||
|
|
||||||
observeConfig: ->
|
observeConfig: ->
|
||||||
@subscribe atom.config.observe 'editor.useHardwareAcceleration', @setUseHardwareAcceleration
|
@subscribe atom.config.observe 'editor.useHardwareAcceleration', @setUseHardwareAcceleration
|
||||||
|
@subscribe atom.config.onDidChange 'editor.fontSize', @sampleFontStyling
|
||||||
|
@subscribe atom.config.onDidChange 'editor.fontFamily', @sampleFontStyling
|
||||||
|
@subscribe atom.config.onDidChange 'editor.lineHeight', @sampleFontStyling
|
||||||
|
|
||||||
onGrammarChanged: ->
|
onGrammarChanged: ->
|
||||||
{editor} = @props
|
{editor} = @props
|
||||||
@@ -428,8 +429,14 @@ TextEditorComponent = React.createClass
|
|||||||
subscriptions.add atom.config.observe scopeDescriptor, 'editor.showLineNumbers', @setShowLineNumbers
|
subscriptions.add atom.config.observe scopeDescriptor, 'editor.showLineNumbers', @setShowLineNumbers
|
||||||
subscriptions.add atom.config.observe scopeDescriptor, 'editor.scrollSensitivity', @setScrollSensitivity
|
subscriptions.add atom.config.observe scopeDescriptor, 'editor.scrollSensitivity', @setScrollSensitivity
|
||||||
|
|
||||||
onFocus: ->
|
focused: ->
|
||||||
@refs.input.focus() if @isMounted()
|
if @isMounted()
|
||||||
|
@setState(focused: true)
|
||||||
|
@refs.input.focus()
|
||||||
|
|
||||||
|
blurred: ->
|
||||||
|
if @isMounted()
|
||||||
|
@setState(focused: false)
|
||||||
|
|
||||||
onTextInput: (event) ->
|
onTextInput: (event) ->
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@@ -452,12 +459,6 @@ TextEditorComponent = React.createClass
|
|||||||
|
|
||||||
inputNode.value = event.data if editor.insertText(event.data)
|
inputNode.value = event.data if editor.insertText(event.data)
|
||||||
|
|
||||||
onInputFocused: ->
|
|
||||||
@setState(focused: true)
|
|
||||||
|
|
||||||
onInputBlurred: ->
|
|
||||||
@setState(focused: false)
|
|
||||||
|
|
||||||
onVerticalScroll: (scrollTop) ->
|
onVerticalScroll: (scrollTop) ->
|
||||||
{editor} = @props
|
{editor} = @props
|
||||||
|
|
||||||
@@ -621,11 +622,11 @@ TextEditorComponent = React.createClass
|
|||||||
else
|
else
|
||||||
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
|
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
|
||||||
|
|
||||||
onStylesheetsChanged: (stylesheet) ->
|
onStylesheetsChanged: (styleElement) ->
|
||||||
return unless @performedInitialMeasurement
|
return unless @performedInitialMeasurement
|
||||||
return unless atom.themes.isInitialLoadComplete()
|
return unless atom.themes.isInitialLoadComplete()
|
||||||
|
|
||||||
@refreshScrollbars() if not stylesheet? or @containsScrollbarSelector(stylesheet)
|
@refreshScrollbars() if not styleElement.sheet? or @containsScrollbarSelector(styleElement.sheet)
|
||||||
@sampleFontStyling()
|
@sampleFontStyling()
|
||||||
@sampleBackgroundColors()
|
@sampleBackgroundColors()
|
||||||
@remeasureCharacterWidths()
|
@remeasureCharacterWidths()
|
||||||
@@ -771,10 +772,10 @@ TextEditorComponent = React.createClass
|
|||||||
measureHeightAndWidth: ->
|
measureHeightAndWidth: ->
|
||||||
return unless @isMounted()
|
return unless @isMounted()
|
||||||
|
|
||||||
{editor, parentView} = @props
|
{editor, hostElement} = @props
|
||||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||||
{position} = getComputedStyle(parentView)
|
{position} = getComputedStyle(hostElement)
|
||||||
{height} = parentView.style
|
{height} = hostElement.style
|
||||||
|
|
||||||
if position is 'absolute' or height
|
if position is 'absolute' or height
|
||||||
if @autoHeight
|
if @autoHeight
|
||||||
@@ -806,9 +807,9 @@ TextEditorComponent = React.createClass
|
|||||||
@remeasureCharacterWidths()
|
@remeasureCharacterWidths()
|
||||||
|
|
||||||
sampleBackgroundColors: (suppressUpdate) ->
|
sampleBackgroundColors: (suppressUpdate) ->
|
||||||
{parentView} = @props
|
{hostElement} = @props
|
||||||
{showLineNumbers} = @state
|
{showLineNumbers} = @state
|
||||||
{backgroundColor} = getComputedStyle(parentView)
|
{backgroundColor} = getComputedStyle(hostElement)
|
||||||
|
|
||||||
if backgroundColor isnt @backgroundColor
|
if backgroundColor isnt @backgroundColor
|
||||||
@backgroundColor = backgroundColor
|
@backgroundColor = backgroundColor
|
||||||
@@ -907,10 +908,10 @@ TextEditorComponent = React.createClass
|
|||||||
lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow)
|
lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow)
|
||||||
|
|
||||||
screenRowForNode: (node) ->
|
screenRowForNode: (node) ->
|
||||||
while node isnt document
|
while node?
|
||||||
if screenRow = node.dataset.screenRow
|
if screenRow = node.dataset.screenRow
|
||||||
return parseInt(screenRow)
|
return parseInt(screenRow)
|
||||||
node = node.parentNode
|
node = node.parentElement
|
||||||
null
|
null
|
||||||
|
|
||||||
getFontSize: ->
|
getFontSize: ->
|
||||||
@@ -977,11 +978,13 @@ TextEditorComponent = React.createClass
|
|||||||
|
|
||||||
updateParentViewFocusedClassIfNeeded: (prevState) ->
|
updateParentViewFocusedClassIfNeeded: (prevState) ->
|
||||||
if prevState.focused isnt @state.focused
|
if prevState.focused isnt @state.focused
|
||||||
@props.parentView.classList.toggle('is-focused', @state.focused)
|
@props.hostElement.classList.toggle('is-focused', @state.focused)
|
||||||
|
@props.rootElement.classList.toggle('is-focused', @state.focused)
|
||||||
|
|
||||||
updateParentViewMiniClassIfNeeded: (prevProps) ->
|
updateParentViewMiniClassIfNeeded: (prevProps) ->
|
||||||
if prevProps.mini isnt @props.mini
|
if prevProps.mini isnt @props.mini
|
||||||
@props.parentView.classList.toggle('mini', @props.mini)
|
@props.hostElement.classList.toggle('mini', @props.mini)
|
||||||
|
@props.rootElement.classList.toggle('mini', @props.mini)
|
||||||
|
|
||||||
runScrollBenchmark: ->
|
runScrollBenchmark: ->
|
||||||
unless process.env.NODE_ENV is 'production'
|
unless process.env.NODE_ENV is 'production'
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ TextEditor = require './text-editor'
|
|||||||
TextEditorComponent = require './text-editor-component'
|
TextEditorComponent = require './text-editor-component'
|
||||||
TextEditorView = null
|
TextEditorView = null
|
||||||
|
|
||||||
|
GlobalStylesElement = null
|
||||||
|
|
||||||
class TextEditorElement extends HTMLElement
|
class TextEditorElement extends HTMLElement
|
||||||
model: null
|
model: null
|
||||||
componentDescriptor: null
|
componentDescriptor: null
|
||||||
@@ -18,13 +20,34 @@ class TextEditorElement extends HTMLElement
|
|||||||
@initializeContent()
|
@initializeContent()
|
||||||
@createSpacePenShim()
|
@createSpacePenShim()
|
||||||
@addEventListener 'focus', @focused.bind(this)
|
@addEventListener 'focus', @focused.bind(this)
|
||||||
@addEventListener 'focusout', @focusedOut.bind(this)
|
|
||||||
@addEventListener 'blur', @blurred.bind(this)
|
@addEventListener 'blur', @blurred.bind(this)
|
||||||
|
|
||||||
initializeContent: (attributes) ->
|
initializeContent: (attributes) ->
|
||||||
@classList.add('editor', 'react', 'editor-colors')
|
@classList.add('editor')
|
||||||
@setAttribute('tabindex', -1)
|
@setAttribute('tabindex', -1)
|
||||||
|
|
||||||
|
if atom.config.get('editor.useShadowDOM')
|
||||||
|
@createShadowRoot()
|
||||||
|
|
||||||
|
@stylesElement = document.createElement('atom-styles')
|
||||||
|
@stylesElement.setAttribute('context', 'atom-text-editor')
|
||||||
|
@stylesElement.initialize()
|
||||||
|
|
||||||
|
@rootElement = document.createElement('div')
|
||||||
|
@rootElement.classList.add('editor', 'editor-colors')
|
||||||
|
|
||||||
|
@shadowRoot.appendChild(@stylesElement)
|
||||||
|
@shadowRoot.appendChild(@rootElement)
|
||||||
|
else
|
||||||
|
unless GlobalStylesElement?
|
||||||
|
GlobalStylesElement = document.createElement('atom-styles')
|
||||||
|
GlobalStylesElement.setAttribute('context', 'atom-text-editor')
|
||||||
|
GlobalStylesElement.initialize()
|
||||||
|
document.head.appendChild(GlobalStylesElement)
|
||||||
|
|
||||||
|
@stylesElement = GlobalStylesElement
|
||||||
|
@rootElement = this
|
||||||
|
|
||||||
createSpacePenShim: ->
|
createSpacePenShim: ->
|
||||||
TextEditorView ?= require './text-editor-view'
|
TextEditorView ?= require './text-editor-view'
|
||||||
@__spacePenView = new TextEditorView(this)
|
@__spacePenView = new TextEditorView(this)
|
||||||
@@ -64,12 +87,19 @@ class TextEditorElement extends HTMLElement
|
|||||||
|
|
||||||
mountComponent: ->
|
mountComponent: ->
|
||||||
@componentDescriptor ?= TextEditorComponent(
|
@componentDescriptor ?= TextEditorComponent(
|
||||||
parentView: this
|
hostElement: this
|
||||||
|
rootElement: @rootElement
|
||||||
|
stylesElement: @stylesElement
|
||||||
editor: @model
|
editor: @model
|
||||||
mini: @model.mini
|
mini: @model.mini
|
||||||
lineOverdrawMargin: @lineOverdrawMargin
|
lineOverdrawMargin: @lineOverdrawMargin
|
||||||
)
|
)
|
||||||
@component = React.renderComponent(@componentDescriptor, this)
|
@component = React.renderComponent(@componentDescriptor, @rootElement)
|
||||||
|
|
||||||
|
unless atom.config.get('editor.useShadowDOM')
|
||||||
|
inputNode = @component.refs.input.getDOMNode()
|
||||||
|
inputNode.addEventListener 'focus', @focused.bind(this)
|
||||||
|
inputNode.addEventListener 'blur', => @dispatchEvent(new FocusEvent('blur', bubbles: false))
|
||||||
|
|
||||||
unmountComponent: ->
|
unmountComponent: ->
|
||||||
return unless @component?.isMounted()
|
return unless @component?.isMounted()
|
||||||
@@ -79,15 +109,17 @@ class TextEditorElement extends HTMLElement
|
|||||||
|
|
||||||
focused: ->
|
focused: ->
|
||||||
if @component?
|
if @component?
|
||||||
@component.onFocus()
|
@component.focused()
|
||||||
else
|
else
|
||||||
@focusOnAttach = true
|
@focusOnAttach = true
|
||||||
|
|
||||||
focusedOut: (event) ->
|
|
||||||
event.stopImmediatePropagation() if @contains(event.relatedTarget)
|
|
||||||
|
|
||||||
blurred: (event) ->
|
blurred: (event) ->
|
||||||
event.stopImmediatePropagation() if @contains(event.relatedTarget)
|
unless atom.config.get('editor.useShadowDOM')
|
||||||
|
if event.relatedTarget is @component?.refs.input?.getDOMNode()
|
||||||
|
event.stopImmediatePropagation()
|
||||||
|
return
|
||||||
|
|
||||||
|
@component?.blurred()
|
||||||
|
|
||||||
addGrammarScopeAttribute: ->
|
addGrammarScopeAttribute: ->
|
||||||
grammarScope = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
|
grammarScope = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
|
||||||
|
|||||||
@@ -73,13 +73,26 @@ class TextEditorView extends View
|
|||||||
setModel: (@model) ->
|
setModel: (@model) ->
|
||||||
@editor = @model
|
@editor = @model
|
||||||
|
|
||||||
@scrollView = @find('.scroll-view')
|
@root = $(@element.rootElement)
|
||||||
@underlayer = @find('.highlights').addClass('underlayer')
|
|
||||||
@overlayer = @find('.lines').addClass('overlayer')
|
@scrollView = @root.find('.scroll-view')
|
||||||
@hiddenInput = @.find('.hidden-input')
|
|
||||||
|
|
||||||
|
if atom.config.get('editor.useShadowDOM')
|
||||||
|
@underlayer = $("<div class='underlayer'></div>").appendTo(this)
|
||||||
|
@overlayer = $("<div class='overlayer'></div>").appendTo(this)
|
||||||
|
else
|
||||||
|
@underlayer = @find('.highlights').addClass('underlayer')
|
||||||
|
@overlayer = @find('.lines').addClass('overlayer')
|
||||||
|
|
||||||
|
@hiddenInput = @root.find('.hidden-input')
|
||||||
|
|
||||||
|
@hiddenInput.on = (args...) =>
|
||||||
|
args[0] = 'blur' if args[0] is 'focusout'
|
||||||
|
$::on.apply(this, args)
|
||||||
|
|
||||||
@subscribe atom.config.observe 'editor.showLineNumbers', =>
|
@subscribe atom.config.observe 'editor.showLineNumbers', =>
|
||||||
@gutter = @find('.gutter')
|
@gutter = @root.find('.gutter')
|
||||||
|
|
||||||
@gutter.removeClassFromAllLines = (klass) =>
|
@gutter.removeClassFromAllLines = (klass) =>
|
||||||
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
|
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
|
||||||
@@ -95,6 +108,13 @@ class TextEditorView extends View
|
|||||||
lines.addClass(klass)
|
lines.addClass(klass)
|
||||||
lines.length > 0
|
lines.length > 0
|
||||||
|
|
||||||
|
find: ->
|
||||||
|
shadowResult = @root.find.apply(@root, arguments)
|
||||||
|
if shadowResult.length > 0
|
||||||
|
shadowResult
|
||||||
|
else
|
||||||
|
super
|
||||||
|
|
||||||
# Public: Get the underlying editor model for this view.
|
# Public: Get the underlying editor model for this view.
|
||||||
#
|
#
|
||||||
# Returns an {TextEditor}
|
# Returns an {TextEditor}
|
||||||
@@ -107,7 +127,7 @@ class TextEditorView extends View
|
|||||||
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
||||||
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
|
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
|
||||||
Object.defineProperty @::, 'active', get: -> @is(@getPaneView()?.activeView)
|
Object.defineProperty @::, 'active', get: -> @is(@getPaneView()?.activeView)
|
||||||
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
|
Object.defineProperty @::, 'isFocused', get: -> document.activeElement is @element or document.activeElement is @element.component?.refs.input.getDOMNode()
|
||||||
Object.defineProperty @::, 'mini', get: -> @component?.props.mini
|
Object.defineProperty @::, 'mini', get: -> @component?.props.mini
|
||||||
Object.defineProperty @::, 'component', get: -> @element?.component
|
Object.defineProperty @::, 'component', get: -> @element?.component
|
||||||
|
|
||||||
@@ -164,7 +184,7 @@ class TextEditorView extends View
|
|||||||
appendToLinesView: (view) ->
|
appendToLinesView: (view) ->
|
||||||
view.css('position', 'absolute')
|
view.css('position', 'absolute')
|
||||||
view.css('z-index', 1)
|
view.css('z-index', 1)
|
||||||
@find('.lines').prepend(view)
|
@overlayer.append(view)
|
||||||
|
|
||||||
unmountComponent: ->
|
unmountComponent: ->
|
||||||
React.unmountComponentAtNode(@element) if @component.isMounted()
|
React.unmountComponentAtNode(@element) if @component.isMounted()
|
||||||
|
|||||||
@@ -249,6 +249,9 @@ class ThemeManager
|
|||||||
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
|
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
|
||||||
@requireStylesheet(nativeStylesheetPath)
|
@requireStylesheet(nativeStylesheetPath)
|
||||||
|
|
||||||
|
textEditorStylesPath = path.join(@resourcePath, 'static', 'text-editor-shadow.less')
|
||||||
|
atom.styles.addStyleSheet(@loadLessStylesheet(textEditorStylesPath), sourcePath: textEditorStylesPath, context: 'atom-text-editor')
|
||||||
|
|
||||||
stylesheetElementForId: (id) ->
|
stylesheetElementForId: (id) ->
|
||||||
document.head.querySelector("atom-styles style[source-path=\"#{id}\"]")
|
document.head.querySelector("atom-styles style[source-path=\"#{id}\"]")
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@
|
|||||||
@import "popover-list";
|
@import "popover-list";
|
||||||
@import "messages";
|
@import "messages";
|
||||||
@import "markdown";
|
@import "markdown";
|
||||||
@import "editor";
|
@import "text-editor-light";
|
||||||
@import "select-list";
|
@import "select-list";
|
||||||
@import "syntax";
|
@import "syntax";
|
||||||
@import "utilities";
|
@import "utilities";
|
||||||
|
|||||||
@@ -1,310 +0,0 @@
|
|||||||
@import "ui-variables";
|
|
||||||
@import "octicon-utf-codes";
|
|
||||||
@import "octicon-mixins";
|
|
||||||
|
|
||||||
atom-text-editor.react {
|
|
||||||
.editor-contents {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.underlayer {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lines {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cursor {
|
|
||||||
z-index: 4;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-contents.is-focused .cursor {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cursors.blink-off .cursor {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizontal-scrollbar {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
height: 15px;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
z-index: 3;
|
|
||||||
|
|
||||||
.scrollbar-content {
|
|
||||||
height: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.vertical-scrollbar {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollbar-corner {
|
|
||||||
position: absolute;
|
|
||||||
overflow: auto;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-view {
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-view-content {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gutter {
|
|
||||||
.line-number {
|
|
||||||
white-space: nowrap;
|
|
||||||
padding-left: .5em;
|
|
||||||
|
|
||||||
.icon-right {
|
|
||||||
padding: 0 .4em;
|
|
||||||
&:before {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor.mini {
|
|
||||||
font-size: @input-font-size;
|
|
||||||
line-height: @component-line-height;
|
|
||||||
max-height: @component-line-height + 2; // +2 for borders
|
|
||||||
|
|
||||||
.placeholder-text {
|
|
||||||
position: absolute;
|
|
||||||
color: @text-color-subtle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor {
|
|
||||||
z-index: 0;
|
|
||||||
font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor, .editor-contents {
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: text;
|
|
||||||
display: -webkit-flex;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter .line-number.cursor-line {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter {
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: right;
|
|
||||||
cursor: default;
|
|
||||||
min-width: 1em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter .line-number {
|
|
||||||
padding-left: .5em;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter .line-numbers {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter .line-number.folded.cursor-line {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter .line-number .icon-right {
|
|
||||||
.octicon(chevron-down, 0.8em);
|
|
||||||
display: inline-block;
|
|
||||||
visibility: hidden;
|
|
||||||
padding-left: .1em;
|
|
||||||
padding-right: .5em;
|
|
||||||
opacity: .6;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter:hover .line-number.foldable .icon-right {
|
|
||||||
visibility: visible;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: @chevron-down;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .gutter, atom-text-editor .gutter:hover {
|
|
||||||
.line-number.folded .icon-right {
|
|
||||||
.octicon(chevron-right, 0.8em);
|
|
||||||
visibility: visible;
|
|
||||||
|
|
||||||
&:before { // chevron-right renders too far right compared to chevron-down
|
|
||||||
position: relative;
|
|
||||||
left: -.1em;
|
|
||||||
content: @chevron-right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .fold-marker {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .fold-marker:after {
|
|
||||||
.icon(0.8em, inline);
|
|
||||||
content: @ellipsis;
|
|
||||||
padding-left: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .line.cursor-line .fold-marker:after {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor.is-blurred .line.cursor-line {
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .invisible-character {
|
|
||||||
font-weight: normal !important;
|
|
||||||
font-style: normal !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .indent-guide {
|
|
||||||
display: inline-block;
|
|
||||||
box-shadow: inset 1px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .vertical-scrollbar,
|
|
||||||
atom-text-editor .horizontal-scrollbar {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .vertical-scrollbar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 15px;
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .scroll-view {
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
-webkit-flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor.soft-wrap .scroll-view {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .underlayer {
|
|
||||||
z-index: 0;
|
|
||||||
position: absolute;
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .lines {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .overlayer {
|
|
||||||
z-index: 2;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .line {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .line span {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .cursor {
|
|
||||||
position: absolute;
|
|
||||||
border-left: 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .cursor,
|
|
||||||
atom-text-editor.is-focused .cursor.blink-off {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor.is-focused .cursor {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cursor.hidden-cursor {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .hidden-input {
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
opacity: 0;
|
|
||||||
width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .highlight {
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor .highlight .region,
|
|
||||||
atom-text-editor .selection .region {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
atom-text-editor.mini:not(.react) {
|
|
||||||
height: auto;
|
|
||||||
line-height: 25px;
|
|
||||||
|
|
||||||
.cursor {
|
|
||||||
width: 2px;
|
|
||||||
line-height: 20px;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gutter {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-view {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
@import "ui-variables";
|
||||||
|
|
||||||
|
atom-text-editor {
|
||||||
|
display: block;
|
||||||
|
font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
atom-text-editor.mini {
|
||||||
|
font-size: @input-font-size;
|
||||||
|
line-height: @component-line-height;
|
||||||
|
max-height: @component-line-height + 2; // +2 for borders
|
||||||
|
}
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
@import "ui-variables";
|
||||||
|
@import "octicon-utf-codes";
|
||||||
|
@import "octicon-mixins";
|
||||||
|
|
||||||
|
.editor, .editor-contents {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlayer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lines {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor {
|
||||||
|
z-index: 4;
|
||||||
|
pointer-events: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursors.blink-off .cursor {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-scrollbar {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
height: 15px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
|
.scrollbar-content {
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-scrollbar {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-corner {
|
||||||
|
position: absolute;
|
||||||
|
overflow: auto;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view {
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter {
|
||||||
|
.line-number {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: .5em;
|
||||||
|
|
||||||
|
.icon-right {
|
||||||
|
padding: 0 .4em;
|
||||||
|
&:before {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-text {
|
||||||
|
position: absolute;
|
||||||
|
color: @text-color-subtle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor, .editor-contents {
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: text;
|
||||||
|
display: -webkit-flex;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter .line-number.cursor-line {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter {
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: right;
|
||||||
|
cursor: default;
|
||||||
|
min-width: 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter .line-number {
|
||||||
|
padding-left: .5em;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter .line-numbers {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter .line-number.folded.cursor-line {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter .line-number .icon-right {
|
||||||
|
.octicon(chevron-down, 0.8em);
|
||||||
|
display: inline-block;
|
||||||
|
visibility: hidden;
|
||||||
|
padding-left: .1em;
|
||||||
|
padding-right: .5em;
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter:hover .line-number.foldable .icon-right {
|
||||||
|
visibility: visible;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: @chevron-down;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter, .gutter:hover {
|
||||||
|
.line-number.folded .icon-right {
|
||||||
|
.octicon(chevron-right, 0.8em);
|
||||||
|
visibility: visible;
|
||||||
|
|
||||||
|
&:before { // chevron-right renders too far right compared to chevron-down
|
||||||
|
position: relative;
|
||||||
|
left: -.1em;
|
||||||
|
content: @chevron-right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fold-marker {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fold-marker:after {
|
||||||
|
.icon(0.8em, inline);
|
||||||
|
content: @ellipsis;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line.cursor-line .fold-marker:after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
atom-text-editor::shadow.is-blurred .line.cursor-line {
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.invisible-character {
|
||||||
|
font-weight: normal !important;
|
||||||
|
font-style: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent-guide {
|
||||||
|
display: inline-block;
|
||||||
|
box-shadow: inset 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-scrollbar,
|
||||||
|
.horizontal-scrollbar {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-scrollbar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 15px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
atom-text-editor::shadow.soft-wrap .scroll-view {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlayer {
|
||||||
|
z-index: 0;
|
||||||
|
position: absolute;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lines {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlayer {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line span {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor {
|
||||||
|
position: absolute;
|
||||||
|
border-left: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-focused .cursor {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-focused .cursor.blink-off {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor.hidden-cursor {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-input {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight .region,
|
||||||
|
.selection .region {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
Referência em uma Nova Issue
Bloquear um usuário