Collection.icon can be a literal emoji or a string key into Outline's own icon library (~50 custom outline-icons keys + ~90 FontAwesome keys, per shared/utils/IconLibrary.tsx in outline/outline — a different mapping than the outline-icons package itself, which is keyed by PascalCase component name, not the string stored on the collection). Only emoji was handled before, so most non-emoji collections fell back to a generic folder glyph. Covers the high-confidence subset with an unambiguous SF Symbol match; unmapped keys keep falling back to the folder rather than risk an invalid symbol name rendering blank.
45 lines
1.6 KiB
Swift
45 lines
1.6 KiB
Swift
import SwiftUI
|
|
import OutlineKit
|
|
|
|
struct CollectionRowView: View {
|
|
let collection: OutlineCollection
|
|
|
|
var body: some View {
|
|
Label {
|
|
Text(collection.name)
|
|
} icon: {
|
|
if let emoji = collection.emojiIcon {
|
|
Text(emoji)
|
|
} else if let symbolName = collection.icon.flatMap(OutlineIconMapping.sfSymbolName) {
|
|
Image(systemName: symbolName)
|
|
.foregroundStyle(tintColor)
|
|
} else {
|
|
Image(systemName: "folder.fill")
|
|
.foregroundStyle(tintColor)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var tintColor: Color {
|
|
collection.color.flatMap { Color(hex: $0) } ?? .accentColor
|
|
}
|
|
}
|
|
|
|
private extension OutlineCollection {
|
|
/// Outline's `icon` field holds either a literal emoji, or a string key
|
|
/// into Outline's own icon set (see OutlineIconMapping) — only the former
|
|
/// is renderable directly, the latter needs the lookup table.
|
|
var emojiIcon: String? {
|
|
// `isEmojiPresentation` misses symbol-class emoji that default to text
|
|
// presentation (e.g. some picked without a variation selector) — `isEmoji`
|
|
// is the broader, correct check for "this scalar can be an emoji at all".
|
|
// Plain ASCII digits/`#`/`*` also report `isEmoji == true` (they're valid
|
|
// keycap-sequence bases), so exclude the ASCII range to avoid treating a
|
|
// literal digit or punctuation icon name as an emoji.
|
|
guard let icon, icon.unicodeScalars.contains(where: { $0.properties.isEmoji && $0.value > 0x7F }) else {
|
|
return nil
|
|
}
|
|
return icon
|
|
}
|
|
}
|