IControl CreateLeftPane( LiveProject project, ElementContext elementContext, IContext context, IShell fileSystem, IClassExtractor classExtractor) { return(Modal.Host(modalHost => { var makeClassViewModel = new ExtractClassButtonViewModel( project, context, dialogModel => ExtractClassView.OpenDialog(modalHost, dialogModel), fileSystem, classExtractor); var hierarchy = TreeView.Create( new TreeViewModel( context, makeClassViewModel.HighlightSelectedElement, elementContext.CreateMenu)); return Layout.Dock() .Bottom(Toolbox.Toolbox.Create(project, elementContext)) .Top(ExtractClassView.CreateButton(makeClassViewModel)) .Fill(hierarchy); })); }
public ExtractClassButtonViewModel( IProject project, IContext context, Action <IExtractClassViewModel> openDialogAction, IFileSystem fileSystem, IClassExtractor classExtractor) { _hovering = new BehaviorSubject <bool>(false); var allClassNames = project.Classes.Select( elements => { var elementList = elements as IList <IElement> ?? elements.ToList(); if (!elementList.Any()) { return(Observable.Return(new List <Optional <string> >())); } return(elementList.Select(el => el.UxClass()).CombineLatest()); }).Switch().Select(x => new HashSet <string>(x.NotNone())).Replay(1).RefCount(); var canExtractSelectedElement = context.CurrentSelection.IsEmpty.IsFalse() .And(context.CurrentSelection.Parent.IsEmpty.IsFalse()) // Not the App(root) element // We can't extract an element which is already a class .And(context.CurrentSelection.UxClass().Is(Optional.None())) // Extracting classes listed in _invalidUxClassNames creates invalid UX .And(context.CurrentSelection.Name.Select(name => !_invalidUxClassNames.Contains(name))) .Replay(1) .RefCount(); _highlightSelectedElement = _hovering.And(canExtractSelectedElement); _command = Command.Create( canExtractSelectedElement, context.CurrentSelection.Name.CombineLatest( allClassNames, (selectedElementName, classNames) => (Action)(() => { openDialogAction( new ExtractClassViewModel( context: context, suggestedName: GetClassNameSuggestion(selectedElementName, classNames), allClassNames: allClassNames, classExtractor: classExtractor, fileSystem: fileSystem, project: project)); }))); }
public ExtractClassViewModel( IContext context, string suggestedName, IObservable <HashSet <string> > allClassNames, IClassExtractor classExtractor, IFileSystem fileSystem, IProject project) { _className = Property.Create(suggestedName); _createInNewFile = Property.Create(false); var newFileNameSubject = new BehaviorSubject <string>(""); var hasEditedFileName = false; _newFileName = newFileNameSubject.AsProperty( isReadOnly: _createInNewFile.Select(check => check == false), write: (name, save) => { hasEditedFileName = true; newFileNameSubject.OnNext(name); }); _className.Subscribe( name => { if (!hasEditedFileName) { var filename = name.Replace(".", System.IO.Path.DirectorySeparatorChar.ToString()) + ".ux"; newFileNameSubject.OnNext(filename); } }); var action = _className.CombineLatest( allClassNames, _createInNewFile, _newFileName, project.RootDirectory, (name, allClasses, toNewFile, fileName, projectPath) => { // Don't care about whitespace at beginning or end name = name.Trim(); if (name.IsNullOrEmpty()) { return(new ValidatedAction("")); } if (name.Any(char.IsWhiteSpace)) { return(new ValidatedAction("Class name can't contain whitespaces")); } if (!ClassNameValidationRegexp.IsMatch(name)) { return(new ValidatedAction("Class name is not valid")); } if (allClasses.Contains(name)) { return(new ValidatedAction("Class name already in use")); } if (toNewFile) { var relativePath = RelativeFilePath.TryParse(fileName); if (!relativePath.HasValue) { return(new ValidatedAction("Not a valid filename")); } if (!fileName.EndsWith(".ux")) { return(new ValidatedAction("Filename must end in .ux")); } if (fileSystem.Exists(projectPath.Combine(relativePath.Value))) { return(new ValidatedAction("The file already exists")); } if (fileName.IsNullOrEmpty()) { return(new ValidatedAction("Filename can not be empty")); } } return(new ValidatedAction( () => { // A Task is returned by this method, however, we don't do anything with it // and just let it finish in the background classExtractor.ExtractClass( element: context.CurrentSelection, name: name, fileName: toNewFile ? Optional.Some(RelativeFilePath.Parse(fileName)) : Optional.None()); })); }) .Replay(1).RefCount(); _userInfo = action.Select(x => x.Message); _createCommand = Command.Create(action.Select(x => x.Action)); }