diff --git a/dev/docs/assets/snippets.css b/dev/docs/assets/snippets.css index 4fb200addb..e5ee48f170 100644 --- a/dev/docs/assets/snippets.css +++ b/dev/docs/assets/snippets.css @@ -83,6 +83,26 @@ font-family: courier, lucidia; } +.anchor-container { + position: relative; +} + +.anchor-button-overlay { + position: absolute; + top: 0px; + right: 5px; + height: 28px; + width: 28px; + transition: .3s ease; + background-color: #2372a3; +} + +.anchor-button { + border-style: none; + background: none; + cursor: pointer; +} + /* Styles for the copy-to-clipboard button */ .copyable-container { position: relative; diff --git a/dev/docs/assets/snippets.js b/dev/docs/assets/snippets.js index 9d4da921da..86e9a2c117 100644 --- a/dev/docs/assets/snippets.js +++ b/dev/docs/assets/snippets.js @@ -57,6 +57,30 @@ function supportsCopying() { !!document.queryCommandSupported('copy'); } +// Copies the given string to the clipboard. +function copyStringToClipboard(string) { + var textArea = document.createElement("textarea"); + textArea.value = string; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + if (!supportsCopying()) { + alert('Unable to copy to clipboard (not supported by browser)'); + return; + } + + try { + document.execCommand('copy'); + } finally { + document.body.removeChild(textArea); + } +} + +function fixHref(anchor, id) { + anchor.href = window.location.href.replace(/#.*$/, '') + '#' + id; +} + // Copies the text inside the currently visible snippet to the clipboard, or the // given element, if any. function copyTextToClipboard(element) { diff --git a/dev/snippets/config/skeletons/application.html b/dev/snippets/config/skeletons/application.html index 7d7e17bab4..56b648cc03 100644 --- a/dev/snippets/config/skeletons/application.html +++ b/dev/snippets/config/skeletons/application.html @@ -1,4 +1,13 @@ {@inject-html} + +
+ + link + +
diff --git a/dev/snippets/lib/main.dart b/dev/snippets/lib/main.dart index 786833d161..996b186c9b 100644 --- a/dev/snippets/lib/main.dart +++ b/dev/snippets/lib/main.dart @@ -152,13 +152,13 @@ void main(List argList) { input, snippetType, template: template, - id: id.join('.'), output: args[_kOutputOption] != null ? File(args[_kOutputOption]) : null, metadata: { 'sourcePath': environment['SOURCE_PATH'], 'sourceLine': environment['SOURCE_LINE'] != null ? int.tryParse(environment['SOURCE_LINE']) : null, + 'id': id.join('.'), 'serial': serial, 'package': packageName, 'library': libraryName, diff --git a/dev/snippets/lib/snippets.dart b/dev/snippets/lib/snippets.dart index 9f69daec2a..05d54f4c16 100644 --- a/dev/snippets/lib/snippets.dart +++ b/dev/snippets/lib/snippets.dart @@ -5,8 +5,9 @@ import 'dart:convert'; import 'dart:io'; -import 'package:path/path.dart' as path; import 'package:dart_style/dart_style.dart'; +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as path; import 'configuration.dart'; @@ -128,13 +129,12 @@ class SnippetGenerator { 'code': htmlEscape.convert(result.join('\n')), 'language': language ?? 'dart', 'serial': '', - 'id': '', + 'id': metadata['id'], 'app': '', }; if (type == SnippetType.application) { substitutions - ..['serial'] = metadata['serial'].toString() ?? '0' - ..['id'] = injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'id').mergedContent + ..['serial'] = metadata['serial']?.toString() ?? '0' ..['app'] = htmlEscape.convert(injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'app').mergedContent); } return skeleton.replaceAllMapped(RegExp('{{(${substitutions.keys.join('|')})}}'), (Match match) { @@ -209,9 +209,15 @@ class SnippetGenerator { /// The [id] is a string ID to use for the output file, and to tell the user /// about in the `flutter create` hint. It must not be null if the [type] is /// [SnippetType.application]. - String generate(File input, SnippetType type, {String template, String id, File output, Map metadata}) { + String generate( + File input, + SnippetType type, { + String template, + File output, + @required Map metadata, + }) { assert(template != null || type != SnippetType.application); - assert(id != null || type != SnippetType.application); + assert(metadata != null && metadata['id'] != null); assert(input != null); final List<_ComponentTuple> snippetData = parseInput(_loadFileAsUtf8(input)); switch (type) { @@ -227,7 +233,6 @@ class SnippetGenerator { 'The template $template was not found in the templates directory ${templatesDir.path}'); exit(1); } - snippetData.add(_ComponentTuple('id', [id])); final String templateContents = _loadFileAsUtf8(templateFile); String app = interpolateTemplate(snippetData, templateContents); @@ -239,7 +244,7 @@ class SnippetGenerator { } snippetData.add(_ComponentTuple('app', app.split('\n'))); - final File outputFile = output ?? getOutputFile(id); + final File outputFile = output ?? getOutputFile(metadata['id']); stderr.writeln('Writing to ${outputFile.absolute.path}'); outputFile.writeAsStringSync(app); @@ -252,7 +257,7 @@ class SnippetGenerator { ); metadata ??= {}; metadata.addAll({ - 'id': id, + 'id': metadata['id'], 'file': path.basename(outputFile.path), 'description': description?.mergedContent, }); diff --git a/dev/snippets/test/snippets_test.dart b/dev/snippets/test/snippets_test.dart index 6408623cac..f3cb758a3c 100644 --- a/dev/snippets/test/snippets_test.dart +++ b/dev/snippets/test/snippets_test.dart @@ -74,8 +74,14 @@ void main() { ``` '''); - final String html = - generator.generate(inputFile, SnippetType.application, template: 'template', id: 'id'); + final String html = generator.generate( + inputFile, + SnippetType.application, + template: 'template', + metadata: { + 'id': 'id', + }, + ); expect(html, contains('
HTML Bits
')); expect(html, contains('
More HTML Bits
')); expect(html, contains('print('The actual \$name.');')); @@ -103,7 +109,7 @@ void main() { ``` '''); - final String html = generator.generate(inputFile, SnippetType.sample); + final String html = generator.generate(inputFile, SnippetType.sample, metadata: {'id': 'id'}); expect(html, contains('
HTML Bits
')); expect(html, contains('
More HTML Bits
')); expect(html, contains(' print('The actual \$name.');')); @@ -135,9 +141,8 @@ void main() { inputFile, SnippetType.application, template: 'template', - id: 'id', output: outputFile, - metadata: {'sourcePath': 'some/path.dart'}, + metadata: {'sourcePath': 'some/path.dart', 'id': 'id'}, ); expect(expectedMetadataFile.existsSync(), isTrue); final Map json = jsonDecode(expectedMetadataFile.readAsStringSync());